Skip to content

Commit 684cda3

Browse files
authored
Don't deduce values for explicitly-specified generic bindings. (#4415)
Distinguish between deduction against a symbolic binding pattern and deduction against a symbolic binding name. In the former case, the value is being explicitly specified and must be constant. In the latter case we encountered a use of the binding name as a subexpression, and should deduce against it if it's not explicitly specified.
1 parent b5a837a commit 684cda3

File tree

8 files changed

+378
-119
lines changed

8 files changed

+378
-119
lines changed

toolchain/check/deduce.cpp

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "toolchain/check/deduce.h"
66

7+
#include "llvm/ADT/SmallBitVector.h"
78
#include "toolchain/base/kind_switch.h"
89
#include "toolchain/check/context.h"
910
#include "toolchain/check/convert.h"
@@ -183,6 +184,8 @@ class DeductionContext {
183184
llvm::SmallVector<SemIR::InstId> result_arg_ids_;
184185
llvm::SmallVector<Substitution> substitutions_;
185186
SemIR::CompileTimeBindIndex first_deduced_index_;
187+
// Non-deduced indexes, indexed by parameter index - first_deduced_index_.
188+
llvm::SmallBitVector non_deduced_indexes_;
186189
};
187190

188191
} // namespace
@@ -231,12 +234,33 @@ DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
231234
}
232235
first_deduced_index_ = SemIR::CompileTimeBindIndex(args.size());
233236
}
237+
238+
non_deduced_indexes_.resize(result_arg_ids_.size() -
239+
first_deduced_index_.index);
234240
}
235241

236242
auto DeductionContext::Deduce() -> bool {
237243
while (!worklist_.Done()) {
238244
auto [param_id, arg_id, needs_substitution] = worklist_.PopNext();
239245

246+
auto note_initializing_param = [&](auto& builder) {
247+
if (auto param =
248+
context().insts().TryGetAs<SemIR::SymbolicBindingPattern>(
249+
param_id)) {
250+
CARBON_DIAGNOSTIC(InitializingGenericParam, Note,
251+
"initializing generic parameter `{0}` declared here",
252+
SemIR::NameId);
253+
builder.Note(
254+
param_id, InitializingGenericParam,
255+
context().entity_names().Get(param->entity_name_id).name_id);
256+
} else {
257+
NoteGenericHere(context(), generic_id_, builder);
258+
}
259+
};
260+
261+
// TODO: Bail out if there's nothing to deduce: if we're not in a pattern
262+
// and the parameter doesn't have a symbolic constant value.
263+
240264
// If the parameter has a symbolic type, deduce against that.
241265
auto param_type_id = context().insts().Get(param_id).type_id();
242266
if (param_type_id.AsConstantId().is_symbolic()) {
@@ -246,54 +270,70 @@ auto DeductionContext::Deduce() -> bool {
246270
} else {
247271
// The argument needs to have the same type as the parameter.
248272
// TODO: Suppress diagnostics here if diagnose_ is false.
249-
DiagnosticAnnotationScope annotate_diagnostics(
250-
&context().emitter(), [&](auto& builder) {
251-
if (auto param =
252-
context().insts().TryGetAs<SemIR::BindSymbolicName>(
253-
param_id)) {
254-
CARBON_DIAGNOSTIC(
255-
InitializingGenericParam, Note,
256-
"initializing generic parameter `{0}` declared here",
257-
SemIR::NameId);
258-
builder.Note(
259-
param_id, InitializingGenericParam,
260-
context().entity_names().Get(param->entity_name_id).name_id);
261-
}
262-
});
273+
// TODO: Only do this when deducing against a symbolic pattern.
274+
DiagnosticAnnotationScope annotate_diagnostics(&context().emitter(),
275+
note_initializing_param);
263276
arg_id = ConvertToValueOfType(context(), loc_id_, arg_id, param_type_id);
264277
if (arg_id == SemIR::InstId::BuiltinError) {
265278
return false;
266279
}
267280
}
268281

269-
// If the parameter is a symbolic constant, deduce against it. Otherwise, we
270-
// assume there is nothing to deduce.
271-
// TODO: This won't do the right thing in a template deduction.
272-
auto param_const_id = context().constant_values().Get(param_id);
273-
if (!param_const_id.is_valid() || !param_const_id.is_symbolic()) {
274-
continue;
275-
}
276-
277282
// Attempt to match `param_inst` against `arg_id`. If the match succeeds,
278283
// this should `continue` the outer loop. On `break`, we will try to desugar
279284
// the parameter to continue looking for a match.
280-
auto param_inst = context().insts().Get(
281-
context().constant_values().GetInstId(param_const_id));
285+
auto param_inst = context().insts().Get(param_id);
282286
CARBON_KIND_SWITCH(param_inst) {
283-
// Deducing a symbolic binding from an argument with a constant value
284-
// deduces the binding as having that constant value.
285-
case SemIR::InstKind::SymbolicBindingPattern:
286-
case SemIR::InstKind::BindSymbolicName: {
287-
auto entity_name_id = SemIR::EntityNameId::Invalid;
288-
if (auto bind = param_inst.TryAs<SemIR::SymbolicBindingPattern>()) {
289-
entity_name_id = bind->entity_name_id;
290-
} else {
291-
entity_name_id =
292-
param_inst.As<SemIR::BindSymbolicName>().entity_name_id;
287+
// Deducing a symbolic binding pattern from an argument deduces the
288+
// binding as having that constant value. For example, deducing
289+
// `(T:! type)` against `(i32)` deduces `T` to be `i32`. This only arises
290+
// when initializing a generic parameter from an explicitly specified
291+
// argument, and in this case, the argument is required to be a
292+
// compile-time constant.
293+
case CARBON_KIND(SemIR::SymbolicBindingPattern bind): {
294+
auto& entity_name = context().entity_names().Get(bind.entity_name_id);
295+
auto index = entity_name.bind_index;
296+
if (!index.is_valid()) {
297+
break;
298+
}
299+
CARBON_CHECK(
300+
index >= first_deduced_index_ &&
301+
static_cast<size_t>(index.index) < result_arg_ids_.size(),
302+
"Unexpected index {0} for symbolic binding pattern; "
303+
"expected to be in range [{1}, {2})",
304+
index.index, first_deduced_index_.index, result_arg_ids_.size());
305+
CARBON_CHECK(!result_arg_ids_[index.index].is_valid(),
306+
"Deduced a value for parameter prior to its declaration");
307+
308+
auto arg_const_inst_id =
309+
context().constant_values().GetConstantInstId(arg_id);
310+
if (!arg_const_inst_id.is_valid()) {
311+
if (diagnose_) {
312+
CARBON_DIAGNOSTIC(CompTimeArgumentNotConstant, Error,
313+
"argument for generic parameter is not a "
314+
"compile-time constant");
315+
auto diag =
316+
context().emitter().Build(loc_id_, CompTimeArgumentNotConstant);
317+
note_initializing_param(diag);
318+
diag.Emit();
319+
}
320+
return false;
293321
}
294-
auto& entity_name = context().entity_names().Get(entity_name_id);
322+
323+
result_arg_ids_[index.index] = arg_const_inst_id;
324+
// This parameter index should not be deduced if it appears later.
325+
non_deduced_indexes_[index.index - first_deduced_index_.index] = true;
326+
continue;
327+
}
328+
329+
// Deducing a symbolic binding appearing within an expression against a
330+
// constant value deduces the binding as having that value. For example,
331+
// deducing `[T:! type](x: T)` against `("foo")` deduces `T` as `String`.
332+
case CARBON_KIND(SemIR::BindSymbolicName bind): {
333+
auto& entity_name = context().entity_names().Get(bind.entity_name_id);
295334
auto index = entity_name.bind_index;
296-
if (!index.is_valid() || index < first_deduced_index_) {
335+
if (!index.is_valid() || index < first_deduced_index_ ||
336+
non_deduced_indexes_[index.index - first_deduced_index_.index]) {
297337
break;
298338
}
299339

@@ -324,6 +364,11 @@ auto DeductionContext::Deduce() -> bool {
324364
continue;
325365
}
326366

367+
case CARBON_KIND(SemIR::ParamPattern pattern): {
368+
Add(pattern.subpattern_id, arg_id, needs_substitution);
369+
continue;
370+
}
371+
327372
// Various kinds of parameter should match an argument of the same form,
328373
// if the operands all match.
329374
case SemIR::ArrayType::Kind:
@@ -358,6 +403,20 @@ auto DeductionContext::Deduce() -> bool {
358403
break;
359404
}
360405

406+
// We didn't manage to deduce against the syntactic form of the parameter.
407+
// Convert it to a canonical constant value and try deducing against that.
408+
auto param_const_id = context().constant_values().Get(param_id);
409+
if (!param_const_id.is_valid() || !param_const_id.is_symbolic()) {
410+
// It's not a symbolic constant. There's nothing here to deduce.
411+
continue;
412+
}
413+
auto param_const_inst_id =
414+
context().constant_values().GetInstId(param_const_id);
415+
if (param_const_inst_id != param_id) {
416+
Add(param_const_inst_id, arg_id, needs_substitution);
417+
continue;
418+
}
419+
361420
// If we've not yet substituted into the parameter, do so now and try again.
362421
if (needs_substitution) {
363422
param_const_id = SubstConstant(context(), param_const_id, substitutions_);

toolchain/check/testdata/class/generic/call.carbon

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,15 @@ library "[[@TEST_NAME]]";
5555

5656
class Class(T:! type, N:! i32) {}
5757

58-
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:8: error: cannot implicitly convert from `i32` to `type`
58+
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+9]]:8: error: cannot implicitly convert from `i32` to `type`
5959
// CHECK:STDERR: var a: Class(5, i32*);
6060
// CHECK:STDERR: ^~~~~~
61-
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+3]]:8: note: type `i32` does not implement interface `ImplicitAs`
61+
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+6]]:8: note: type `i32` does not implement interface `ImplicitAs`
6262
// CHECK:STDERR: var a: Class(5, i32*);
6363
// CHECK:STDERR: ^~~~~~
64+
// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE-8]]:1: note: while deducing parameters of generic declared here
65+
// CHECK:STDERR: class Class(T:! type, N:! i32) {}
66+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6467
var a: Class(5, i32*);
6568

6669
// --- call_in_nested_return_type.carbon
@@ -463,15 +466,15 @@ class Outer(T:! type) {
463466
// CHECK:STDOUT: %N.loc4_23.1: i32 = bind_symbolic_name N, 1, %param.loc4_23 [symbolic = %N.loc4_23.2 (constants.%N)]
464467
// CHECK:STDOUT: }
465468
// CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, %Class.decl [template = constants.%Class.1]
466-
// CHECK:STDOUT: %.loc12_14: i32 = int_literal 5 [template = constants.%.4]
469+
// CHECK:STDOUT: %.loc15_14: i32 = int_literal 5 [template = constants.%.4]
467470
// CHECK:STDOUT: %int.make_type_32: init type = call constants.%Int32() [template = i32]
468-
// CHECK:STDOUT: %.loc12_20.1: type = value_of_initializer %int.make_type_32 [template = i32]
469-
// CHECK:STDOUT: %.loc12_20.2: type = converted %int.make_type_32, %.loc12_20.1 [template = i32]
470-
// CHECK:STDOUT: %.loc12_20.3: type = ptr_type i32 [template = constants.%.5]
471+
// CHECK:STDOUT: %.loc15_20.1: type = value_of_initializer %int.make_type_32 [template = i32]
472+
// CHECK:STDOUT: %.loc15_20.2: type = converted %int.make_type_32, %.loc15_20.1 [template = i32]
473+
// CHECK:STDOUT: %.loc15_20.3: type = ptr_type i32 [template = constants.%.5]
471474
// CHECK:STDOUT: %ImplicitAs.type: type = interface_type @ImplicitAs, @ImplicitAs(type) [template = constants.%ImplicitAs.type.3]
472-
// CHECK:STDOUT: %.loc12_13.1: %.8 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.9]
473-
// CHECK:STDOUT: %Convert.ref: %.8 = name_ref Convert, %.loc12_13.1 [template = constants.%.9]
474-
// CHECK:STDOUT: %.loc12_13.2: type = converted %.loc12_14, <error> [template = <error>]
475+
// CHECK:STDOUT: %.loc15_13.1: %.8 = specific_constant imports.%import_ref.4, @ImplicitAs(type) [template = constants.%.9]
476+
// CHECK:STDOUT: %Convert.ref: %.8 = name_ref Convert, %.loc15_13.1 [template = constants.%.9]
477+
// CHECK:STDOUT: %.loc15_13.2: type = converted %.loc15_14, <error> [template = <error>]
475478
// CHECK:STDOUT: %a.var: ref <error> = var a
476479
// CHECK:STDOUT: %a: ref <error> = bind_name a, %a.var
477480
// CHECK:STDOUT: }

0 commit comments

Comments
 (0)