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(
"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) {
return Declare(name, std::unique_ptr<Namespace>(new Namespace(name)));
}
......
......@@ -78,6 +78,8 @@ class Declarations {
static GenericStructType* LookupUniqueGenericStructType(
const QualifiedName& name);
static base::Optional<GenericStructType*> TryLookupGenericStructType(
const QualifiedName& name);
static Namespace* DeclareNamespace(const std::string& name);
static TypeAlias* DeclareType(const Identifier* name, const Type* type);
......
This diff is collapsed.
......@@ -365,27 +365,26 @@ class ImplementationVisitor {
VisitResult Visit(Expression* expr);
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(
const AggregateType* aggregate,
const ClassType* class_type,
const std::vector<NameAndExpression>& expressions);
void InitializeFieldFromSpread(VisitResult object, const Field& field,
const InitializerResults& initializer_results);
size_t InitializeAggregateHelper(
const AggregateType* aggregate_type, VisitResult allocate_result,
const InitializerResults& initializer_results);
VisitResult AddVariableObjectSize(
VisitResult object_size, const ClassType* current_class,
const InitializerResults& initializer_results);
void InitializeAggregate(const AggregateType* aggregate_type,
VisitResult allocate_result,
const InitializerResults& initializer_results);
void InitializeClass(const ClassType* class_type, VisitResult allocate_result,
const InitializerResults& initializer_results);
VisitResult TemporaryUninitializedStruct(const StructType* struct_type,
const std::string& reason);
VisitResult Visit(StructExpression* decl);
LocationReference GetLocationReference(Expression* location);
......
......@@ -11,7 +11,7 @@ namespace torque {
TypeArgumentInference::TypeArgumentInference(
const NameVector& type_parameters,
const TypeVector& explicit_type_arguments,
const std::vector<TypeExpression*> term_parameters,
const std::vector<TypeExpression*>& term_parameters,
const TypeVector& term_argument_types)
: num_explicit_(explicit_type_arguments.size()),
type_parameter_from_name_(type_parameters.size()),
......@@ -84,7 +84,7 @@ void TypeArgumentInference::Match(TypeExpression* parameter,
// argument types, but we are only interested in inferring type arguments
// here
} else {
Fail("unsupported parameter expression");
// TODO(gsps): Perform inference on function and union types
}
}
......
......@@ -46,12 +46,13 @@ namespace torque {
// Pick<Smi>(1, aSmi); // inference succeeds (doing nothing)
//
// 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 {
public:
TypeArgumentInference(const NameVector& type_parameters,
const TypeVector& explicit_type_arguments,
const std::vector<TypeExpression*> term_parameters,
const std::vector<TypeExpression*>& term_parameters,
const TypeVector& term_argument_types);
bool HasFailed() const { return failure_reason_.has_value(); }
......
......@@ -8,6 +8,7 @@
#include "src/torque/declarable.h"
#include "src/torque/global-context.h"
#include "src/torque/server-data.h"
#include "src/torque/type-inference.h"
#include "src/torque/type-oracle.h"
namespace v8 {
......@@ -121,6 +122,10 @@ const StructType* TypeVisitor::ComputeType(
CurrentSourcePosition::Scope position_activator(
field.name_and_type.type->pos);
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,
base::nullopt,
......@@ -316,6 +321,53 @@ void TypeVisitor::VisitClassFieldsAndMethods(
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 internal
} // namespace v8
......@@ -28,6 +28,9 @@ class TypeVisitor {
static void VisitClassFieldsAndMethods(
ClassType* class_type, const ClassDeclaration* class_declaration);
static Signature MakeSignature(const CallableNodeSignature* signature);
static const StructType* ComputeTypeForStructExpression(
TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types);
private:
friend class TypeAlias;
......
......@@ -418,6 +418,17 @@ void ClassType::Finalize() const {
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() {
// For each field, construct AST snippets that implement a CSA accessor
// function and define a corresponding '.field' operator. The
......
......@@ -550,6 +550,8 @@ class ClassType final : public AggregateType {
}
void Finalize() const override;
std::vector<Field> ComputeAllFields() const;
private:
friend class TypeOracle;
friend class TypeVisitor;
......
......@@ -580,7 +580,7 @@ TEST(TestGenericStruct2) {
i::HandleScope scope(isolate);
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{ m.Return(m.TestGenericStruct2().fst); }
{ m.Return(m.TestGenericStruct2().snd.fst); }
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
......
......@@ -980,8 +980,8 @@ namespace test {
@export
macro TestGenericStruct1(): intptr {
const i: intptr = 123;
let box = SBox<intptr>{value: i};
let boxbox = SBox<SBox<intptr>>{value: box};
let box = SBox{value: i};
let boxbox: SBox<SBox<intptr>> = SBox{value: box};
check(box.value == 123);
boxbox.value.value *= 2;
check(boxbox.value.value == 246);
......@@ -995,16 +995,19 @@ namespace test {
macro TupleSwap<T1: type, T2: type>(tuple: TestTuple<T1, T2>):
TestTuple<T2, T1> {
return TestTuple<T2, T1>{fst: tuple.snd, snd: tuple.fst};
return TestTuple{fst: tuple.snd, snd: tuple.fst};
}
@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 smiAndIntptr = TupleSwap(intptrAndSmi);
check(intptrAndSmi.fst == smiAndIntptr.snd);
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 {
......
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