Skip to content

Commit 925250f

Browse files
zygoloidjonmeow
andauthored
Improve diagnostics for overload resolution failure. (#6091)
Include notes listing the candidates and explaining why they didn't work. Rather than duplicating the (substantial) logic for this, use the Clang machinery to generate these diagnostics. In order to support this, add a mechanism to map `SemIR::LocId`s to `clang::SourceLocation`s. This works by creating source buffers in Clang that refer into the Carbon source file so that `SourceLocation`s can point into them. --------- Co-authored-by: Jon Ross-Perkins <[email protected]>
1 parent ef488f0 commit 925250f

21 files changed

+443
-349
lines changed

toolchain/check/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ cc_library(
2323
"convert.cpp",
2424
"cpp/custom_type_mapping.cpp",
2525
"cpp/import.cpp",
26+
"cpp/location.cpp",
2627
"cpp/operators.cpp",
2728
"cpp/overload_resolution.cpp",
2829
"cpp/thunk.cpp",
@@ -73,6 +74,7 @@ cc_library(
7374
"convert.h",
7475
"cpp/custom_type_mapping.h",
7576
"cpp/import.h",
77+
"cpp/location.h",
7678
"cpp/operators.h",
7779
"cpp/overload_resolution.h",
7880
"cpp/thunk.h",
@@ -148,6 +150,7 @@ cc_library(
148150
"//toolchain/lex:tokenized_buffer",
149151
"//toolchain/parse:node_kind",
150152
"//toolchain/parse:tree",
153+
"//toolchain/sem_ir:absolute_node_id",
151154
"//toolchain/sem_ir:clang_decl",
152155
"//toolchain/sem_ir:expr_info",
153156
"//toolchain/sem_ir:file",

toolchain/check/context.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ class Context {
157157
return import_ir_constant_values_;
158158
}
159159

160+
auto cpp_carbon_file_locations()
161+
-> llvm::SmallVector<clang::SourceLocation>& {
162+
return cpp_carbon_file_locations_;
163+
}
164+
160165
auto definitions_required_by_decl() -> llvm::SmallVector<SemIR::InstId>& {
161166
return definitions_required_by_decl_;
162167
}
@@ -387,6 +392,10 @@ class Context {
387392
// Inline 0 elements because it's expected to require heap allocation.
388393
llvm::SmallVector<SemIR::ConstantValueStore, 0> import_ir_constant_values_;
389394

395+
// Per-Carbon-file start locations for corresponding Clang source buffers.
396+
// Owned and managed by code in cpp/location.cpp.
397+
llvm::SmallVector<clang::SourceLocation> cpp_carbon_file_locations_;
398+
390399
// Declaration instructions of entities that should have definitions by the
391400
// end of the current source file.
392401
llvm::SmallVector<SemIR::InstId> definitions_required_by_decl_;

toolchain/check/cpp/import.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,13 @@ auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
16671667

16681668
SemIR::Function& function_info = context.functions().Get(*function_id);
16691669
if (IsCppThunkRequired(context, function_info)) {
1670+
Diagnostics::AnnotationScope annotate_diagnostics(
1671+
&context.emitter(), [&](auto& builder) {
1672+
CARBON_DIAGNOSTIC(InCppThunk, Note,
1673+
"in thunk for C++ function used here");
1674+
builder.Note(loc_id, InCppThunk);
1675+
});
1676+
16701677
clang::FunctionDecl* thunk_clang_decl =
16711678
BuildCppThunk(context, function_info);
16721679
if (thunk_clang_decl) {

toolchain/check/cpp/location.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "toolchain/check/cpp/location.h"
6+
7+
#include "toolchain/sem_ir/absolute_node_id.h"
8+
#include "toolchain/sem_ir/ids.h"
9+
10+
namespace Carbon::Check {
11+
12+
namespace {
13+
struct FileInfo {
14+
const SemIR::File* sem_ir;
15+
clang::SourceLocation start_loc;
16+
};
17+
} // namespace
18+
19+
// Map a CheckIRId into information about the corresponding file in both SemIR
20+
// and Clang's source manager.
21+
static auto GetFileInfo(Context& context, SemIR::CheckIRId ir_id) -> FileInfo {
22+
const SemIR::File* sem_ir = &context.sem_ir();
23+
int file_index = 0;
24+
25+
// If the file is imported, locate it in our imports map.
26+
if (ir_id != context.sem_ir().check_ir_id()) {
27+
auto import_id = context.check_ir_map().Get(ir_id);
28+
CARBON_CHECK(import_id.has_value());
29+
file_index = import_id.index + 1;
30+
31+
sem_ir = context.import_irs().Get(import_id).sem_ir;
32+
CARBON_CHECK(sem_ir, "Node location in nonexistent IR");
33+
}
34+
35+
// If we've seen this file before, reuse the same FileID.
36+
auto& file_start_locs = context.cpp_carbon_file_locations();
37+
if (static_cast<int>(file_start_locs.size()) <= file_index) {
38+
// Never valid; prepare a slot for the caching below.
39+
file_start_locs.resize(file_index + 1);
40+
} else if (file_start_locs[file_index].isValid()) {
41+
return {.sem_ir = sem_ir, .start_loc = file_start_locs[file_index]};
42+
}
43+
44+
// We've not seen this file before. Create a corresponding virtual file in
45+
// Clang's source manager.
46+
// TODO: Consider recreating the complete import path instead of only the
47+
// final entry.
48+
const auto& source = sem_ir->parse_tree().tokens().source();
49+
auto& src_mgr = context.ast_context().getSourceManager();
50+
auto file_id = src_mgr.createFileID(
51+
llvm::MemoryBufferRef(source.text(), source.filename()));
52+
auto file_start_loc = src_mgr.getLocForStartOfFile(file_id);
53+
file_start_locs[file_index] = file_start_loc;
54+
return {.sem_ir = sem_ir, .start_loc = file_start_loc};
55+
}
56+
57+
auto GetCppLocation(Context& context, SemIR::LocId loc_id)
58+
-> clang::SourceLocation {
59+
if (!context.sem_ir().clang_ast_unit()) {
60+
return clang::SourceLocation();
61+
}
62+
63+
// Break down the `LocId` into an import path. If that ends in a C++ location,
64+
// we can just return that directly.
65+
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
66+
SemIR::GetAbsoluteNodeId(&context.sem_ir(), loc_id);
67+
if (absolute_node_ids.back().check_ir_id() == SemIR::CheckIRId::Cpp) {
68+
return context.sem_ir().clang_source_locs().Get(
69+
absolute_node_ids.back().clang_source_loc_id());
70+
}
71+
72+
// This is a location in Carbon code; get or create a corresponding file in
73+
// Clang and build a corresponding location.
74+
auto absolute_node_id = absolute_node_ids.back();
75+
auto [ir, start_loc] = GetFileInfo(context, absolute_node_id.check_ir_id());
76+
const auto& tree = ir->parse_tree();
77+
auto offset =
78+
tree.tokens().GetByteOffset(tree.node_token(absolute_node_id.node_id()));
79+
return start_loc.getLocWithOffset(offset);
80+
}
81+
82+
} // namespace Carbon::Check

toolchain/check/cpp/location.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_
6+
#define CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_
7+
8+
#include "toolchain/check/context.h"
9+
#include "toolchain/sem_ir/ids.h"
10+
11+
namespace Carbon::Check {
12+
13+
// Maps a Carbon source location into an equivalent Clang source location.
14+
auto GetCppLocation(Context& context, SemIR::LocId loc_id)
15+
-> clang::SourceLocation;
16+
17+
} // namespace Carbon::Check
18+
19+
#endif // CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_

toolchain/check/cpp/overload_resolution.cpp

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,27 @@
44

55
#include "toolchain/check/cpp/overload_resolution.h"
66

7+
#include "clang/Basic/DiagnosticSema.h"
78
#include "clang/Sema/Overload.h"
89
#include "clang/Sema/Sema.h"
910
#include "toolchain/check/cpp/import.h"
11+
#include "toolchain/check/cpp/location.h"
1012
#include "toolchain/check/cpp/type_mapping.h"
1113
#include "toolchain/sem_ir/ids.h"
1214
#include "toolchain/sem_ir/typed_insts.h"
1315

1416
namespace Carbon::Check {
1517

18+
// Map a Carbon name into a C++ name.
19+
static auto GetCppName(Context& context, SemIR::NameId name_id)
20+
-> clang::DeclarationName {
21+
// TODO: Some special names should probably use different formatting. In
22+
// particular, NameId::CppOperator should probably map back to a
23+
// CXXOperatorName.
24+
auto name_str = context.names().GetFormatted(name_id);
25+
return clang::DeclarationName(&context.ast_context().Idents.get(name_str));
26+
}
27+
1628
// Adds the given overload candidates to the candidate set.
1729
static auto AddOverloadCandidataes(clang::Sema& sema,
1830
clang::OverloadCandidateSet& candidate_set,
@@ -67,13 +79,6 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
6779
SemIR::InstId self_id,
6880
llvm::ArrayRef<SemIR::InstId> arg_ids)
6981
-> SemIR::InstId {
70-
Diagnostics::AnnotationScope annotate_diagnostics(
71-
&context.emitter(), [&](auto& builder) {
72-
CARBON_DIAGNOSTIC(InCallToCppFunction, Note,
73-
"in call to Cpp function here");
74-
builder.Note(loc_id, InCallToCppFunction);
75-
});
76-
7782
// Map Carbon call argument types to C++ types.
7883
clang::Expr* self_expr = nullptr;
7984
if (self_id.has_value()) {
@@ -82,67 +87,63 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
8287
return SemIR::ErrorInst::InstId;
8388
}
8489
}
85-
auto arg_exprs = InventClangArgs(context, arg_ids);
86-
if (!arg_exprs.has_value()) {
90+
auto maybe_arg_exprs = InventClangArgs(context, arg_ids);
91+
if (!maybe_arg_exprs.has_value()) {
8792
return SemIR::ErrorInst::InstId;
8893
}
94+
auto& arg_exprs = *maybe_arg_exprs;
8995

9096
const SemIR::CppOverloadSet& overload_set =
9197
context.cpp_overload_sets().Get(overload_set_id);
9298

99+
clang::SourceLocation loc = GetCppLocation(context, loc_id);
100+
93101
// Add candidate functions from the name lookup.
94102
clang::OverloadCandidateSet candidate_set(
95-
// TODO: Add location accordingly.
96-
clang::SourceLocation(),
97-
clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
103+
loc, clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
98104

99105
clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
100106
CARBON_CHECK(ast);
101107
clang::Sema& sema = ast->getSema();
102108

103109
AddOverloadCandidataes(sema, candidate_set, overload_set.candidate_functions,
104-
self_expr, *arg_exprs);
110+
self_expr, arg_exprs);
105111

106112
// Find best viable function among the candidates.
107113
clang::OverloadCandidateSet::iterator best_viable_fn;
108114
clang::OverloadingResult overloading_result =
109-
// TODO: Add location accordingly.
110-
candidate_set.BestViableFunction(sema, clang::SourceLocation(),
111-
best_viable_fn);
115+
candidate_set.BestViableFunction(sema, loc, best_viable_fn);
112116

113117
switch (overloading_result) {
114118
case clang::OverloadingResult::OR_Success: {
115119
// TODO: Handle the cases when Function is null.
116120
CARBON_CHECK(best_viable_fn->Function);
117-
sema.MarkFunctionReferenced(clang::SourceLocation(),
118-
best_viable_fn->Function);
121+
sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
119122
SemIR::InstId result =
120123
ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function);
121124
return result;
122125
}
123126
case clang::OverloadingResult::OR_No_Viable_Function: {
124-
// TODO: Add notes with the candidates.
125-
CARBON_DIAGNOSTIC(CppOverloadingNoViableFunctionFound, Error,
126-
"no matching function for call to `{0}`",
127-
SemIR::NameId);
128-
context.emitter().Emit(loc_id, CppOverloadingNoViableFunctionFound,
129-
overload_set.name_id);
127+
candidate_set.NoteCandidates(
128+
clang::PartialDiagnosticAt(
129+
loc, sema.PDiag(clang::diag::err_ovl_no_viable_function_in_call)
130+
<< GetCppName(context, overload_set.name_id)),
131+
sema, clang::OCD_AllCandidates, arg_exprs);
130132
return SemIR::ErrorInst::InstId;
131133
}
132134
case clang::OverloadingResult::OR_Ambiguous: {
133-
// TODO: Add notes with the candidates.
134-
CARBON_DIAGNOSTIC(CppOverloadingAmbiguousCandidatesFound, Error,
135-
"call to `{0}` is ambiguous", SemIR::NameId);
136-
context.emitter().Emit(loc_id, CppOverloadingAmbiguousCandidatesFound,
137-
overload_set.name_id);
135+
candidate_set.NoteCandidates(
136+
clang::PartialDiagnosticAt(
137+
loc, sema.PDiag(clang::diag::err_ovl_ambiguous_call)
138+
<< GetCppName(context, overload_set.name_id)),
139+
sema, clang::OCD_AmbiguousCandidates, arg_exprs);
138140
return SemIR::ErrorInst::InstId;
139141
}
140142
case clang::OverloadingResult::OR_Deleted: {
141-
// TODO: Add notes with the candidates.
142-
CARBON_DIAGNOSTIC(CppOverloadingDeletedFunctionFound, Error,
143-
"call to deleted function `{0}`", SemIR::NameId);
144-
context.emitter().Emit(loc_id, CppOverloadingDeletedFunctionFound,
145-
overload_set.name_id);
143+
sema.DiagnoseUseOfDeletedFunction(
144+
loc, clang::SourceRange(loc, loc),
145+
GetCppName(context, overload_set.name_id), candidate_set,
146+
best_viable_fn->Function, arg_exprs);
146147
return SemIR::ErrorInst::InstId;
147148
}
148149
}

toolchain/check/cpp/type_mapping.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "toolchain/base/value_ids.h"
1717
#include "toolchain/check/context.h"
1818
#include "toolchain/check/convert.h"
19+
#include "toolchain/check/cpp/location.h"
1920
#include "toolchain/check/literal.h"
2021
#include "toolchain/sem_ir/class.h"
2122
#include "toolchain/sem_ir/expr_info.h"
@@ -272,9 +273,9 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
272273

273274
// TODO: Avoid heap allocating more of these on every call. Either cache them
274275
// somewhere or put them on the stack.
275-
return new (context.ast_context()) clang::OpaqueValueExpr(
276-
// TODO: Add location accordingly.
277-
clang::SourceLocation(), arg_cpp_type.getNonReferenceType(), value_kind);
276+
return new (context.ast_context())
277+
clang::OpaqueValueExpr(GetCppLocation(context, SemIR::LocId(arg_id)),
278+
arg_cpp_type.getNonReferenceType(), value_kind);
278279
}
279280

280281
auto InventClangArgs(Context& context, llvm::ArrayRef<SemIR::InstId> arg_ids)

0 commit comments

Comments
 (0)