Commit b1db8d84 authored by Georg Schmid's avatar Georg Schmid Committed by Commit Bot

[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: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63036}
parent f2f0562f
...@@ -139,6 +139,13 @@ GenericStructType* Declarations::LookupUniqueGenericStructType( ...@@ -139,6 +139,13 @@ GenericStructType* Declarations::LookupUniqueGenericStructType(
"generic struct"); "generic struct");
} }
base::Optional<GenericStructType*> Declarations::TryLookupGenericStructType(
const QualifiedName& name) {
std::vector<GenericStructType*> results = TryLookup<GenericStructType>(name);
if (results.empty()) return base::nullopt;
return EnsureUnique(results, name.name, "generic struct");
}
Namespace* Declarations::DeclareNamespace(const std::string& name) { Namespace* Declarations::DeclareNamespace(const std::string& name) {
return Declare(name, std::unique_ptr<Namespace>(new Namespace(name))); return Declare(name, std::unique_ptr<Namespace>(new Namespace(name)));
} }
......
...@@ -78,6 +78,8 @@ class Declarations { ...@@ -78,6 +78,8 @@ class Declarations {
static GenericStructType* LookupUniqueGenericStructType( static GenericStructType* LookupUniqueGenericStructType(
const QualifiedName& name); const QualifiedName& name);
static base::Optional<GenericStructType*> TryLookupGenericStructType(
const QualifiedName& name);
static Namespace* DeclareNamespace(const std::string& name); static Namespace* DeclareNamespace(const std::string& name);
static TypeAlias* DeclareType(const Identifier* name, const Type* type); static TypeAlias* DeclareType(const Identifier* name, const Type* type);
......
This diff is collapsed.
...@@ -365,27 +365,26 @@ class ImplementationVisitor { ...@@ -365,27 +365,26 @@ class ImplementationVisitor {
VisitResult Visit(Expression* expr); VisitResult Visit(Expression* expr);
const Type* Visit(Statement* stmt); const Type* Visit(Statement* stmt);
void CheckInitializersWellformed(
const std::string& aggregate_name,
const std::vector<Field>& aggregate_fields,
const std::vector<NameAndExpression>& initializers,
bool ignore_first_field = false);
InitializerResults VisitInitializerResults( InitializerResults VisitInitializerResults(
const AggregateType* aggregate, const ClassType* class_type,
const std::vector<NameAndExpression>& expressions); const std::vector<NameAndExpression>& expressions);
void InitializeFieldFromSpread(VisitResult object, const Field& field, void InitializeFieldFromSpread(VisitResult object, const Field& field,
const InitializerResults& initializer_results); const InitializerResults& initializer_results);
size_t InitializeAggregateHelper(
const AggregateType* aggregate_type, VisitResult allocate_result,
const InitializerResults& initializer_results);
VisitResult AddVariableObjectSize( VisitResult AddVariableObjectSize(
VisitResult object_size, const ClassType* current_class, VisitResult object_size, const ClassType* current_class,
const InitializerResults& initializer_results); const InitializerResults& initializer_results);
void InitializeAggregate(const AggregateType* aggregate_type, void InitializeClass(const ClassType* class_type, VisitResult allocate_result,
VisitResult allocate_result, const InitializerResults& initializer_results);
const InitializerResults& initializer_results);
VisitResult TemporaryUninitializedStruct(const StructType* struct_type,
const std::string& reason);
VisitResult Visit(StructExpression* decl); VisitResult Visit(StructExpression* decl);
LocationReference GetLocationReference(Expression* location); LocationReference GetLocationReference(Expression* location);
......
...@@ -11,7 +11,7 @@ namespace torque { ...@@ -11,7 +11,7 @@ namespace torque {
TypeArgumentInference::TypeArgumentInference( TypeArgumentInference::TypeArgumentInference(
const NameVector& type_parameters, const NameVector& type_parameters,
const TypeVector& explicit_type_arguments, const TypeVector& explicit_type_arguments,
const std::vector<TypeExpression*> term_parameters, const std::vector<TypeExpression*>& term_parameters,
const TypeVector& term_argument_types) const TypeVector& term_argument_types)
: num_explicit_(explicit_type_arguments.size()), : num_explicit_(explicit_type_arguments.size()),
type_parameter_from_name_(type_parameters.size()), type_parameter_from_name_(type_parameters.size()),
...@@ -84,7 +84,7 @@ void TypeArgumentInference::Match(TypeExpression* parameter, ...@@ -84,7 +84,7 @@ void TypeArgumentInference::Match(TypeExpression* parameter,
// argument types, but we are only interested in inferring type arguments // argument types, but we are only interested in inferring type arguments
// here // here
} else { } else {
Fail("unsupported parameter expression"); // TODO(gsps): Perform inference on function and union types
} }
} }
......
...@@ -46,12 +46,13 @@ namespace torque { ...@@ -46,12 +46,13 @@ namespace torque {
// Pick<Smi>(1, aSmi); // inference succeeds (doing nothing) // Pick<Smi>(1, aSmi); // inference succeeds (doing nothing)
// //
// In the above case the inference simply ignores inconsistent constraints on // In the above case the inference simply ignores inconsistent constraints on
// `T`. // `T`. Similarly, we ignore all constraints arising from formal parameters
// that are function- or union-typed.
class TypeArgumentInference { class TypeArgumentInference {
public: public:
TypeArgumentInference(const NameVector& type_parameters, TypeArgumentInference(const NameVector& type_parameters,
const TypeVector& explicit_type_arguments, const TypeVector& explicit_type_arguments,
const std::vector<TypeExpression*> term_parameters, const std::vector<TypeExpression*>& term_parameters,
const TypeVector& term_argument_types); const TypeVector& term_argument_types);
bool HasFailed() const { return failure_reason_.has_value(); } bool HasFailed() const { return failure_reason_.has_value(); }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/torque/declarable.h" #include "src/torque/declarable.h"
#include "src/torque/global-context.h" #include "src/torque/global-context.h"
#include "src/torque/server-data.h" #include "src/torque/server-data.h"
#include "src/torque/type-inference.h"
#include "src/torque/type-oracle.h" #include "src/torque/type-oracle.h"
namespace v8 { namespace v8 {
...@@ -121,6 +122,10 @@ const StructType* TypeVisitor::ComputeType( ...@@ -121,6 +122,10 @@ const StructType* TypeVisitor::ComputeType(
CurrentSourcePosition::Scope position_activator( CurrentSourcePosition::Scope position_activator(
field.name_and_type.type->pos); field.name_and_type.type->pos);
const Type* field_type = TypeVisitor::ComputeType(field.name_and_type.type); const Type* field_type = TypeVisitor::ComputeType(field.name_and_type.type);
if (field_type->IsConstexpr()) {
ReportError("struct field \"", field.name_and_type.name->value,
"\" carries constexpr type \"", *field_type, "\"");
}
struct_type->RegisterField({field.name_and_type.name->pos, struct_type->RegisterField({field.name_and_type.name->pos,
struct_type, struct_type,
base::nullopt, base::nullopt,
...@@ -316,6 +321,53 @@ void TypeVisitor::VisitClassFieldsAndMethods( ...@@ -316,6 +321,53 @@ void TypeVisitor::VisitClassFieldsAndMethods(
DeclareMethods(class_type, class_declaration->methods); DeclareMethods(class_type, class_declaration->methods);
} }
const StructType* TypeVisitor::ComputeTypeForStructExpression(
TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types) {
auto* basic = BasicTypeExpression::DynamicCast(type_expression);
if (!basic) {
ReportError("expected basic type expression referring to struct");
}
QualifiedName qualified_name{basic->namespace_qualification, basic->name};
base::Optional<GenericStructType*> maybe_generic_struct =
Declarations::TryLookupGenericStructType(qualified_name);
// Compute types of non-generic structs as usual
if (!maybe_generic_struct) {
const Type* type = ComputeType(type_expression);
const StructType* struct_type = StructType::DynamicCast(type);
if (!struct_type) {
ReportError(*type, " is not a struct, but used like one");
}
return struct_type;
}
auto generic_struct = *maybe_generic_struct;
auto explicit_type_arguments = ComputeTypeVector(basic->generic_arguments);
std::vector<TypeExpression*> term_parameters;
auto& fields = generic_struct->declaration()->fields;
term_parameters.reserve(fields.size());
for (auto& field : fields) {
term_parameters.push_back(field.name_and_type.type);
}
TypeArgumentInference inference(
generic_struct->declaration()->generic_parameters,
explicit_type_arguments, term_parameters, term_argument_types);
if (inference.HasFailed()) {
ReportError("failed to infer type arguments for struct ", basic->name,
" initialization: ", inference.GetFailureReason());
}
if (GlobalContext::collect_language_server_data()) {
LanguageServerData::AddDefinition(type_expression->pos,
generic_struct->declaration()->name->pos);
}
return TypeOracle::GetGenericStructTypeInstance(generic_struct,
inference.GetResult());
}
} // namespace torque } // namespace torque
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -28,6 +28,9 @@ class TypeVisitor { ...@@ -28,6 +28,9 @@ class TypeVisitor {
static void VisitClassFieldsAndMethods( static void VisitClassFieldsAndMethods(
ClassType* class_type, const ClassDeclaration* class_declaration); ClassType* class_type, const ClassDeclaration* class_declaration);
static Signature MakeSignature(const CallableNodeSignature* signature); static Signature MakeSignature(const CallableNodeSignature* signature);
static const StructType* ComputeTypeForStructExpression(
TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types);
private: private:
friend class TypeAlias; friend class TypeAlias;
......
...@@ -418,6 +418,17 @@ void ClassType::Finalize() const { ...@@ -418,6 +418,17 @@ void ClassType::Finalize() const {
CheckForDuplicateFields(); CheckForDuplicateFields();
} }
std::vector<Field> ClassType::ComputeAllFields() const {
std::vector<Field> all_fields;
const ClassType* super_class = this->GetSuperClass();
if (super_class) {
all_fields = super_class->ComputeAllFields();
}
const std::vector<Field>& fields = this->fields();
all_fields.insert(all_fields.end(), fields.begin(), fields.end());
return all_fields;
}
void ClassType::GenerateAccessors() { void ClassType::GenerateAccessors() {
// For each field, construct AST snippets that implement a CSA accessor // For each field, construct AST snippets that implement a CSA accessor
// function and define a corresponding '.field' operator. The // function and define a corresponding '.field' operator. The
......
...@@ -550,6 +550,8 @@ class ClassType final : public AggregateType { ...@@ -550,6 +550,8 @@ class ClassType final : public AggregateType {
} }
void Finalize() const override; void Finalize() const override;
std::vector<Field> ComputeAllFields() const;
private: private:
friend class TypeOracle; friend class TypeOracle;
friend class TypeVisitor; friend class TypeVisitor;
......
...@@ -580,7 +580,7 @@ TEST(TestGenericStruct2) { ...@@ -580,7 +580,7 @@ TEST(TestGenericStruct2) {
i::HandleScope scope(isolate); i::HandleScope scope(isolate);
CodeAssemblerTester asm_tester(isolate); CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state()); TestTorqueAssembler m(asm_tester.state());
{ m.Return(m.TestGenericStruct2().fst); } { m.Return(m.TestGenericStruct2().snd.fst); }
FunctionTester ft(asm_tester.GenerateCode(), 0); FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call(); ft.Call();
} }
......
...@@ -980,8 +980,8 @@ namespace test { ...@@ -980,8 +980,8 @@ namespace test {
@export @export
macro TestGenericStruct1(): intptr { macro TestGenericStruct1(): intptr {
const i: intptr = 123; const i: intptr = 123;
let box = SBox<intptr>{value: i}; let box = SBox{value: i};
let boxbox = SBox<SBox<intptr>>{value: box}; let boxbox: SBox<SBox<intptr>> = SBox{value: box};
check(box.value == 123); check(box.value == 123);
boxbox.value.value *= 2; boxbox.value.value *= 2;
check(boxbox.value.value == 246); check(boxbox.value.value == 246);
...@@ -995,16 +995,19 @@ namespace test { ...@@ -995,16 +995,19 @@ namespace test {
macro TupleSwap<T1: type, T2: type>(tuple: TestTuple<T1, T2>): macro TupleSwap<T1: type, T2: type>(tuple: TestTuple<T1, T2>):
TestTuple<T2, T1> { TestTuple<T2, T1> {
return TestTuple<T2, T1>{fst: tuple.snd, snd: tuple.fst}; return TestTuple{fst: tuple.snd, snd: tuple.fst};
} }
@export @export
macro TestGenericStruct2(): TestTuple<Smi, intptr> { macro TestGenericStruct2():
TestTuple<TestTuple<intptr, Smi>, TestTuple<Smi, intptr>> {
const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2}; const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2};
const smiAndIntptr = TupleSwap(intptrAndSmi); const smiAndIntptr = TupleSwap(intptrAndSmi);
check(intptrAndSmi.fst == smiAndIntptr.snd); check(intptrAndSmi.fst == smiAndIntptr.snd);
check(intptrAndSmi.snd == smiAndIntptr.fst); check(intptrAndSmi.snd == smiAndIntptr.fst);
return smiAndIntptr; const tupleTuple =
TestTuple<TestTuple<intptr, Smi>>{fst: intptrAndSmi, snd: smiAndIntptr};
return tupleTuple;
} }
macro BranchAndWriteResult(x: Smi, box: SmiBox): bool { macro BranchAndWriteResult(x: Smi, box: SmiBox): bool {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment