Skip to content

Commit b1db8d8

Browse files
Georg SchmidCommit Bot
authored andcommitted
[torque] Infer type arguments of generic struct initializers
Previously when creating a new generic struct, one had to explicitly provide all type arguments, e.g., for the generic struct struct Box<T: type> { const value: T; } one would initialize a new box using const aSmi: Smi = ...; const box = Box<Smi> { value: aSmi }; With the additions in this CL the explicit type argument can be omitted. Type inference proceeds analogously to specialization of generic callables. Additionally, this CL slightly refactors class and struct initialization, and make type inference more permissive in the presence of unsupported type constructors (concretely, union types and function types). R=jgruber@chromium.org, tebbi@chromium.org Change-Id: I529be5831a85d317d8caa6cb3a0ce398ad578c86 Bug: v8:7793 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1728617 Commit-Queue: Georg Schmid <gsps@google.com> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#63036}
1 parent f2f0562 commit b1db8d8

12 files changed

Lines changed: 173 additions & 111 deletions

src/torque/declarations.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ GenericStructType* Declarations::LookupUniqueGenericStructType(
139139
"generic struct");
140140
}
141141

142+
base::Optional<GenericStructType*> Declarations::TryLookupGenericStructType(
143+
const QualifiedName& name) {
144+
std::vector<GenericStructType*> results = TryLookup<GenericStructType>(name);
145+
if (results.empty()) return base::nullopt;
146+
return EnsureUnique(results, name.name, "generic struct");
147+
}
148+
142149
Namespace* Declarations::DeclareNamespace(const std::string& name) {
143150
return Declare(name, std::unique_ptr<Namespace>(new Namespace(name)));
144151
}

src/torque/declarations.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class Declarations {
7878

7979
static GenericStructType* LookupUniqueGenericStructType(
8080
const QualifiedName& name);
81+
static base::Optional<GenericStructType*> TryLookupGenericStructType(
82+
const QualifiedName& name);
8183

8284
static Namespace* DeclareNamespace(const std::string& name);
8385
static TypeAlias* DeclareType(const Identifier* name, const Type* type);

src/torque/implementation-visitor.cc

Lines changed: 73 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,29 +1111,6 @@ const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
11111111
return TypeOracle::GetNeverType();
11121112
}
11131113

1114-
VisitResult ImplementationVisitor::TemporaryUninitializedStruct(
1115-
const StructType* struct_type, const std::string& reason) {
1116-
StackRange range = assembler().TopRange(0);
1117-
for (const Field& f : struct_type->fields()) {
1118-
if (const StructType* struct_type =
1119-
StructType::DynamicCast(f.name_and_type.type)) {
1120-
range.Extend(
1121-
TemporaryUninitializedStruct(struct_type, reason).stack_range());
1122-
} else {
1123-
std::string descriptor = "uninitialized field '" + f.name_and_type.name +
1124-
"' declared at " + PositionAsString(f.pos) +
1125-
" (" + reason + ")";
1126-
TypeVector lowered_types = LowerType(f.name_and_type.type);
1127-
for (const Type* type : lowered_types) {
1128-
assembler().Emit(PushUninitializedInstruction{
1129-
TypeOracle::GetTopType(descriptor, type)});
1130-
}
1131-
range.Extend(assembler().TopRange(lowered_types.size()));
1132-
}
1133-
}
1134-
return VisitResult(struct_type, range);
1135-
}
1136-
11371114
VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
11381115
size_t parameter_count = expr->label_block->parameters.names.size();
11391116
std::vector<VisitResult> parameters;
@@ -1212,15 +1189,38 @@ VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
12121189
return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
12131190
}
12141191

1192+
void ImplementationVisitor::CheckInitializersWellformed(
1193+
const std::string& aggregate_name,
1194+
const std::vector<Field>& aggregate_fields,
1195+
const std::vector<NameAndExpression>& initializers,
1196+
bool ignore_first_field) {
1197+
size_t fields_offset = ignore_first_field ? 1 : 0;
1198+
size_t fields_size = aggregate_fields.size() - fields_offset;
1199+
for (size_t i = 0; i < std::min(fields_size, initializers.size()); i++) {
1200+
const std::string& field_name =
1201+
aggregate_fields[i + fields_offset].name_and_type.name;
1202+
Identifier* found_name = initializers[i].name;
1203+
if (field_name != found_name->value) {
1204+
Error("Expected field name \"", field_name, "\" instead of \"",
1205+
found_name->value, "\"")
1206+
.Position(found_name->pos)
1207+
.Throw();
1208+
}
1209+
}
1210+
if (fields_size != initializers.size()) {
1211+
ReportError("expected ", fields_size, " initializers for ", aggregate_name,
1212+
" found ", initializers.size());
1213+
}
1214+
}
1215+
12151216
InitializerResults ImplementationVisitor::VisitInitializerResults(
1216-
const AggregateType* current_aggregate,
1217+
const ClassType* class_type,
12171218
const std::vector<NameAndExpression>& initializers) {
12181219
InitializerResults result;
12191220
for (const NameAndExpression& initializer : initializers) {
12201221
result.names.push_back(initializer.name);
12211222
Expression* e = initializer.expression;
1222-
const Field& field =
1223-
current_aggregate->LookupField(initializer.name->value);
1223+
const Field& field = class_type->LookupField(initializer.name->value);
12241224
auto field_index = field.index;
12251225
if (SpreadExpression* s = SpreadExpression::DynamicCast(e)) {
12261226
if (!field_index) {
@@ -1239,54 +1239,30 @@ InitializerResults ImplementationVisitor::VisitInitializerResults(
12391239
return result;
12401240
}
12411241

1242-
size_t ImplementationVisitor::InitializeAggregateHelper(
1243-
const AggregateType* aggregate_type, VisitResult allocate_result,
1242+
void ImplementationVisitor::InitializeClass(
1243+
const ClassType* class_type, VisitResult allocate_result,
12441244
const InitializerResults& initializer_results) {
1245-
const ClassType* current_class = ClassType::DynamicCast(aggregate_type);
1246-
size_t current = 0;
1247-
if (current_class) {
1248-
const ClassType* super = current_class->GetSuperClass();
1249-
if (super) {
1250-
current = InitializeAggregateHelper(super, allocate_result,
1251-
initializer_results);
1252-
}
1245+
if (const ClassType* super = class_type->GetSuperClass()) {
1246+
InitializeClass(super, allocate_result, initializer_results);
12531247
}
12541248

1255-
for (Field f : aggregate_type->fields()) {
1256-
if (current == initializer_results.field_value_map.size()) {
1257-
ReportError("insufficient number of initializers for ",
1258-
aggregate_type->name());
1259-
}
1249+
for (Field f : class_type->fields()) {
12601250
VisitResult current_value =
12611251
initializer_results.field_value_map.at(f.name_and_type.name);
1262-
Identifier* fieldname = initializer_results.names[current];
1263-
if (fieldname->value != f.name_and_type.name) {
1264-
CurrentSourcePosition::Scope scope(fieldname->pos);
1265-
ReportError("Expected fieldname \"", f.name_and_type.name,
1266-
"\" instead of \"", fieldname->value, "\"");
1267-
}
1268-
if (aggregate_type->IsClassType()) {
1269-
if (f.index) {
1270-
InitializeFieldFromSpread(allocate_result, f, initializer_results);
1271-
} else {
1272-
allocate_result.SetType(aggregate_type);
1273-
GenerateCopy(allocate_result);
1274-
assembler().Emit(CreateFieldReferenceInstruction{
1275-
ClassType::cast(aggregate_type), f.name_and_type.name});
1276-
VisitResult heap_reference(
1277-
TypeOracle::GetReferenceType(f.name_and_type.type),
1278-
assembler().TopRange(2));
1279-
GenerateAssignToLocation(
1280-
LocationReference::HeapReference(heap_reference), current_value);
1281-
}
1252+
if (f.index) {
1253+
InitializeFieldFromSpread(allocate_result, f, initializer_results);
12821254
} else {
1283-
LocationReference struct_field_ref = LocationReference::VariableAccess(
1284-
ProjectStructField(allocate_result, f.name_and_type.name));
1285-
GenerateAssignToLocation(struct_field_ref, current_value);
1255+
allocate_result.SetType(class_type);
1256+
GenerateCopy(allocate_result);
1257+
assembler().Emit(CreateFieldReferenceInstruction{
1258+
ClassType::cast(class_type), f.name_and_type.name});
1259+
VisitResult heap_reference(
1260+
TypeOracle::GetReferenceType(f.name_and_type.type),
1261+
assembler().TopRange(2));
1262+
GenerateAssignToLocation(LocationReference::HeapReference(heap_reference),
1263+
current_value);
12861264
}
1287-
++current;
12881265
}
1289-
return current;
12901266
}
12911267

12921268
void ImplementationVisitor::InitializeFieldFromSpread(
@@ -1305,17 +1281,6 @@ void ImplementationVisitor::InitializeFieldFromSpread(
13051281
{field.aggregate, index.type, iterator.type()});
13061282
}
13071283

1308-
void ImplementationVisitor::InitializeAggregate(
1309-
const AggregateType* aggregate_type, VisitResult allocate_result,
1310-
const InitializerResults& initializer_results) {
1311-
size_t consumed_initializers = InitializeAggregateHelper(
1312-
aggregate_type, allocate_result, initializer_results);
1313-
if (consumed_initializers != initializer_results.field_value_map.size()) {
1314-
ReportError("more initializers than fields present in ",
1315-
aggregate_type->name());
1316-
}
1317-
}
1318-
13191284
VisitResult ImplementationVisitor::AddVariableObjectSize(
13201285
VisitResult object_size, const ClassType* current_class,
13211286
const InitializerResults& initializer_results) {
@@ -1398,6 +1363,11 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
13981363
initializer_results.field_value_map[map_field.name_and_type.name] =
13991364
object_map;
14001365
}
1366+
1367+
CheckInitializersWellformed(class_type->name(),
1368+
class_type->ComputeAllFields(),
1369+
expr->initializers, !class_type->IsExtern());
1370+
14011371
Arguments size_arguments;
14021372
size_arguments.parameters.push_back(object_map);
14031373
VisitResult object_size = GenerateCall("%GetAllocationBaseSize",
@@ -1412,7 +1382,7 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
14121382
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
14131383
DCHECK(allocate_result.IsOnStack());
14141384

1415-
InitializeAggregate(class_type, allocate_result, initializer_results);
1385+
InitializeClass(class_type, allocate_result, initializer_results);
14161386

14171387
return stack_scope.Yield(allocate_result);
14181388
}
@@ -1802,24 +1772,36 @@ VisitResult ImplementationVisitor::GenerateCopy(const VisitResult& to_copy) {
18021772

18031773
VisitResult ImplementationVisitor::Visit(StructExpression* expr) {
18041774
StackScope stack_scope(this);
1805-
const Type* raw_type = TypeVisitor::ComputeType(expr->type);
1806-
if (!raw_type->IsStructType()) {
1807-
ReportError(*raw_type, " is not a struct but used like one");
1808-
}
18091775

1810-
const StructType* struct_type = StructType::cast(raw_type);
1776+
auto& initializers = expr->initializers;
1777+
std::vector<VisitResult> values;
1778+
std::vector<const Type*> term_argument_types;
1779+
values.reserve(initializers.size());
1780+
term_argument_types.reserve(initializers.size());
18111781

1812-
InitializerResults initialization_results =
1813-
ImplementationVisitor::VisitInitializerResults(struct_type,
1814-
expr->initializers);
1782+
// Compute values and types of all initializer arguments
1783+
for (const NameAndExpression& initializer : initializers) {
1784+
VisitResult value = Visit(initializer.expression);
1785+
values.push_back(value);
1786+
term_argument_types.push_back(value.type());
1787+
}
18151788

1816-
// Push uninitialized 'this'
1817-
VisitResult result = TemporaryUninitializedStruct(
1818-
struct_type, "it's not initialized in the struct " + struct_type->name());
1789+
// Compute and check struct type from given struct name and argument types
1790+
const StructType* struct_type = TypeVisitor::ComputeTypeForStructExpression(
1791+
expr->type, term_argument_types);
1792+
CheckInitializersWellformed(struct_type->name(), struct_type->fields(),
1793+
initializers);
18191794

1820-
InitializeAggregate(struct_type, result, initialization_results);
1795+
// Implicitly convert values and thereby build the struct on the stack
1796+
StackRange struct_range = assembler().TopRange(0);
1797+
auto& fields = struct_type->fields();
1798+
for (size_t i = 0; i < values.size(); i++) {
1799+
values[i] =
1800+
GenerateImplicitConvert(fields[i].name_and_type.type, values[i]);
1801+
struct_range.Extend(values[i].stack_range());
1802+
}
18211803

1822-
return stack_scope.Yield(result);
1804+
return stack_scope.Yield(VisitResult(struct_type, struct_range));
18231805
}
18241806

18251807
LocationReference ImplementationVisitor::GetLocationReference(

src/torque/implementation-visitor.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -365,27 +365,26 @@ class ImplementationVisitor {
365365
VisitResult Visit(Expression* expr);
366366
const Type* Visit(Statement* stmt);
367367

368+
void CheckInitializersWellformed(
369+
const std::string& aggregate_name,
370+
const std::vector<Field>& aggregate_fields,
371+
const std::vector<NameAndExpression>& initializers,
372+
bool ignore_first_field = false);
373+
368374
InitializerResults VisitInitializerResults(
369-
const AggregateType* aggregate,
375+
const ClassType* class_type,
370376
const std::vector<NameAndExpression>& expressions);
371377

372378
void InitializeFieldFromSpread(VisitResult object, const Field& field,
373379
const InitializerResults& initializer_results);
374380

375-
size_t InitializeAggregateHelper(
376-
const AggregateType* aggregate_type, VisitResult allocate_result,
377-
const InitializerResults& initializer_results);
378-
379381
VisitResult AddVariableObjectSize(
380382
VisitResult object_size, const ClassType* current_class,
381383
const InitializerResults& initializer_results);
382384

383-
void InitializeAggregate(const AggregateType* aggregate_type,
384-
VisitResult allocate_result,
385-
const InitializerResults& initializer_results);
385+
void InitializeClass(const ClassType* class_type, VisitResult allocate_result,
386+
const InitializerResults& initializer_results);
386387

387-
VisitResult TemporaryUninitializedStruct(const StructType* struct_type,
388-
const std::string& reason);
389388
VisitResult Visit(StructExpression* decl);
390389

391390
LocationReference GetLocationReference(Expression* location);

src/torque/type-inference.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace torque {
1111
TypeArgumentInference::TypeArgumentInference(
1212
const NameVector& type_parameters,
1313
const TypeVector& explicit_type_arguments,
14-
const std::vector<TypeExpression*> term_parameters,
14+
const std::vector<TypeExpression*>& term_parameters,
1515
const TypeVector& term_argument_types)
1616
: num_explicit_(explicit_type_arguments.size()),
1717
type_parameter_from_name_(type_parameters.size()),
@@ -84,7 +84,7 @@ void TypeArgumentInference::Match(TypeExpression* parameter,
8484
// argument types, but we are only interested in inferring type arguments
8585
// here
8686
} else {
87-
Fail("unsupported parameter expression");
87+
// TODO(gsps): Perform inference on function and union types
8888
}
8989
}
9090

src/torque/type-inference.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@ namespace torque {
4646
// Pick<Smi>(1, aSmi); // inference succeeds (doing nothing)
4747
//
4848
// In the above case the inference simply ignores inconsistent constraints on
49-
// `T`.
49+
// `T`. Similarly, we ignore all constraints arising from formal parameters
50+
// that are function- or union-typed.
5051
class TypeArgumentInference {
5152
public:
5253
TypeArgumentInference(const NameVector& type_parameters,
5354
const TypeVector& explicit_type_arguments,
54-
const std::vector<TypeExpression*> term_parameters,
55+
const std::vector<TypeExpression*>& term_parameters,
5556
const TypeVector& term_argument_types);
5657

5758
bool HasFailed() const { return failure_reason_.has_value(); }

src/torque/type-visitor.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "src/torque/declarable.h"
99
#include "src/torque/global-context.h"
1010
#include "src/torque/server-data.h"
11+
#include "src/torque/type-inference.h"
1112
#include "src/torque/type-oracle.h"
1213

1314
namespace v8 {
@@ -121,6 +122,10 @@ const StructType* TypeVisitor::ComputeType(
121122
CurrentSourcePosition::Scope position_activator(
122123
field.name_and_type.type->pos);
123124
const Type* field_type = TypeVisitor::ComputeType(field.name_and_type.type);
125+
if (field_type->IsConstexpr()) {
126+
ReportError("struct field \"", field.name_and_type.name->value,
127+
"\" carries constexpr type \"", *field_type, "\"");
128+
}
124129
struct_type->RegisterField({field.name_and_type.name->pos,
125130
struct_type,
126131
base::nullopt,
@@ -316,6 +321,53 @@ void TypeVisitor::VisitClassFieldsAndMethods(
316321
DeclareMethods(class_type, class_declaration->methods);
317322
}
318323

324+
const StructType* TypeVisitor::ComputeTypeForStructExpression(
325+
TypeExpression* type_expression,
326+
const std::vector<const Type*>& term_argument_types) {
327+
auto* basic = BasicTypeExpression::DynamicCast(type_expression);
328+
if (!basic) {
329+
ReportError("expected basic type expression referring to struct");
330+
}
331+
332+
QualifiedName qualified_name{basic->namespace_qualification, basic->name};
333+
base::Optional<GenericStructType*> maybe_generic_struct =
334+
Declarations::TryLookupGenericStructType(qualified_name);
335+
336+
// Compute types of non-generic structs as usual
337+
if (!maybe_generic_struct) {
338+
const Type* type = ComputeType(type_expression);
339+
const StructType* struct_type = StructType::DynamicCast(type);
340+
if (!struct_type) {
341+
ReportError(*type, " is not a struct, but used like one");
342+
}
343+
return struct_type;
344+
}
345+
346+
auto generic_struct = *maybe_generic_struct;
347+
auto explicit_type_arguments = ComputeTypeVector(basic->generic_arguments);
348+
349+
std::vector<TypeExpression*> term_parameters;
350+
auto& fields = generic_struct->declaration()->fields;
351+
term_parameters.reserve(fields.size());
352+
for (auto& field : fields) {
353+
term_parameters.push_back(field.name_and_type.type);
354+
}
355+
356+
TypeArgumentInference inference(
357+
generic_struct->declaration()->generic_parameters,
358+
explicit_type_arguments, term_parameters, term_argument_types);
359+
if (inference.HasFailed()) {
360+
ReportError("failed to infer type arguments for struct ", basic->name,
361+
" initialization: ", inference.GetFailureReason());
362+
}
363+
if (GlobalContext::collect_language_server_data()) {
364+
LanguageServerData::AddDefinition(type_expression->pos,
365+
generic_struct->declaration()->name->pos);
366+
}
367+
return TypeOracle::GetGenericStructTypeInstance(generic_struct,
368+
inference.GetResult());
369+
}
370+
319371
} // namespace torque
320372
} // namespace internal
321373
} // namespace v8

0 commit comments

Comments
 (0)