|
8 | 8 | #include "clang/Sema/Sema.h" |
9 | 9 | #include "toolchain/check/cpp/import.h" |
10 | 10 | #include "toolchain/check/cpp/location.h" |
| 11 | +#include "toolchain/check/cpp/overload_resolution.h" |
11 | 12 | #include "toolchain/check/cpp/type_mapping.h" |
12 | 13 | #include "toolchain/check/inst.h" |
13 | 14 | #include "toolchain/check/type.h" |
14 | 15 | #include "toolchain/check/type_completion.h" |
15 | 16 | #include "toolchain/sem_ir/ids.h" |
| 17 | +#include "toolchain/sem_ir/typed_insts.h" |
16 | 18 |
|
17 | 19 | namespace Carbon::Check { |
18 | 20 |
|
@@ -190,36 +192,105 @@ auto LookupCppOperator(Context& context, SemIR::LocId loc_id, Operator op, |
190 | 192 | } |
191 | 193 | } |
192 | 194 |
|
193 | | - auto arg_exprs = InventClangArgs(context, arg_ids); |
194 | | - if (!arg_exprs.has_value()) { |
| 195 | + auto maybe_arg_exprs = InventClangArgs(context, arg_ids); |
| 196 | + if (!maybe_arg_exprs.has_value()) { |
195 | 197 | return SemIR::ErrorInst::InstId; |
196 | 198 | } |
| 199 | + auto& arg_exprs = *maybe_arg_exprs; |
197 | 200 |
|
198 | 201 | clang::SourceLocation loc = GetCppLocation(context, loc_id); |
199 | 202 | clang::OverloadCandidateSet::OperatorRewriteInfo operator_rewrite_info( |
200 | 203 | *op_kind, loc, /*AllowRewritten=*/true); |
201 | | - clang::UnresolvedSet<4> functions; |
202 | 204 | clang::OverloadCandidateSet candidate_set( |
203 | 205 | loc, clang::OverloadCandidateSet::CSK_Operator, operator_rewrite_info); |
| 206 | + |
| 207 | + clang::Sema& sema = context.clang_sema(); |
| 208 | + |
204 | 209 | // This works for both unary and binary operators. |
205 | | - context.clang_sema().LookupOverloadedBinOp(candidate_set, *op_kind, functions, |
206 | | - *arg_exprs); |
| 210 | + sema.LookupOverloadedBinOp(candidate_set, *op_kind, clang::UnresolvedSet<0>{}, |
| 211 | + arg_exprs); |
207 | 212 |
|
208 | | - for (auto& it : candidate_set) { |
209 | | - if (!it.Function) { |
210 | | - continue; |
| 213 | + clang::OverloadCandidateSet::iterator best_viable_fn; |
| 214 | + switch (candidate_set.BestViableFunction(sema, loc, best_viable_fn)) { |
| 215 | + case clang::OverloadingResult::OR_Success: { |
| 216 | + if (!best_viable_fn->Function) { |
| 217 | + // The best viable candidate was a builtin. Let the Carbon operator |
| 218 | + // machinery handle that. |
| 219 | + return SemIR::InstId::None; |
| 220 | + } |
| 221 | + if (best_viable_fn->RewriteKind) { |
| 222 | + context.TODO( |
| 223 | + loc_id, |
| 224 | + llvm::formatv("Rewriting operator{0} using {1} is not supported", |
| 225 | + clang::getOperatorSpelling( |
| 226 | + candidate_set.getRewriteInfo().OriginalOperator), |
| 227 | + best_viable_fn->Function->getNameAsString())); |
| 228 | + return SemIR::ErrorInst::InstId; |
| 229 | + } |
| 230 | + sema.MarkFunctionReferenced(loc, best_viable_fn->Function); |
| 231 | + auto result_id = ImportCppFunctionDecl( |
| 232 | + context, loc_id, best_viable_fn->Function, |
| 233 | + // If this is an operator method, the first arg will be used as self. |
| 234 | + arg_ids.size() - |
| 235 | + (isa<clang::CXXMethodDecl>(best_viable_fn->Function) ? 1 : 0)); |
| 236 | + if (auto fn_decl = |
| 237 | + context.insts().TryGetAsWithId<SemIR::FunctionDecl>(result_id)) { |
| 238 | + CheckCppOverloadAccess(context, loc_id, best_viable_fn->FoundDecl, |
| 239 | + fn_decl->inst_id); |
| 240 | + } else { |
| 241 | + CARBON_CHECK(result_id == SemIR::ErrorInst::InstId); |
| 242 | + } |
| 243 | + return result_id; |
| 244 | + } |
| 245 | + case clang::OverloadingResult::OR_No_Viable_Function: { |
| 246 | + // OK, didn't find a viable C++ candidate, but this is not an error, as |
| 247 | + // there might be a Carbon candidate. |
| 248 | + return SemIR::InstId::None; |
| 249 | + } |
| 250 | + case clang::OverloadingResult::OR_Ambiguous: { |
| 251 | + const char* spelling = clang::getOperatorSpelling(*op_kind); |
| 252 | + candidate_set.NoteCandidates( |
| 253 | + clang::PartialDiagnosticAt( |
| 254 | + loc, sema.PDiag(clang::diag::err_ovl_ambiguous_oper_binary) |
| 255 | + << spelling << arg_exprs[0]->getType() |
| 256 | + << arg_exprs[1]->getType()), |
| 257 | + sema, clang::OCD_AmbiguousCandidates, arg_exprs, spelling, loc); |
| 258 | + return SemIR::ErrorInst::InstId; |
211 | 259 | } |
212 | | - functions.addDecl(it.Function, it.FoundDecl.getAccess()); |
| 260 | + case clang::OverloadingResult::OR_Deleted: |
| 261 | + const char* spelling = clang::getOperatorSpelling(*op_kind); |
| 262 | + auto* message = best_viable_fn->Function->getDeletedMessage(); |
| 263 | + // The best viable function might be a different operator if the best |
| 264 | + // candidate is a rewritten candidate, so use the operator kind of the |
| 265 | + // candidate itself in the diagnostic. |
| 266 | + candidate_set.NoteCandidates( |
| 267 | + clang::PartialDiagnosticAt( |
| 268 | + loc, sema.PDiag(clang::diag::err_ovl_deleted_oper) |
| 269 | + << clang::getOperatorSpelling( |
| 270 | + best_viable_fn->Function->getOverloadedOperator()) |
| 271 | + << (message != nullptr) |
| 272 | + << (message ? message->getString() : llvm::StringRef())), |
| 273 | + sema, clang::OCD_AllCandidates, arg_exprs, spelling, loc); |
| 274 | + return SemIR::ErrorInst::InstId; |
213 | 275 | } |
214 | | - |
215 | | - return ImportCppOverloadSet( |
216 | | - context, loc_id, SemIR::NameScopeId::None, SemIR::NameId::CppOperator, |
217 | | - /*naming_class=*/nullptr, std::move(functions), operator_rewrite_info); |
218 | 276 | } |
219 | 277 |
|
220 | 278 | auto IsCppOperatorMethodDecl(clang::Decl* decl) -> bool { |
221 | 279 | auto* clang_method_decl = dyn_cast<clang::CXXMethodDecl>(decl); |
222 | 280 | return clang_method_decl && clang_method_decl->isOverloadedOperator(); |
223 | 281 | } |
224 | 282 |
|
| 283 | +auto IsCppOperatorMethod(Context& context, SemIR::InstId inst_id) -> bool { |
| 284 | + auto function_type = context.types().TryGetAs<SemIR::FunctionType>( |
| 285 | + context.insts().Get(inst_id).type_id()); |
| 286 | + if (!function_type) { |
| 287 | + return false; |
| 288 | + } |
| 289 | + SemIR::ClangDeclId clang_decl_id = |
| 290 | + context.functions().Get(function_type->function_id).clang_decl_id; |
| 291 | + return clang_decl_id.has_value() && |
| 292 | + IsCppOperatorMethodDecl( |
| 293 | + context.clang_decls().Get(clang_decl_id).key.decl); |
| 294 | +} |
| 295 | + |
225 | 296 | } // namespace Carbon::Check |
0 commit comments