[torque] Generalize type argument inference for generic calls
With the arrival of generic structs (https://chromium-review.googlesource.com/c/v8/v8/+/1714868) the existing type inference procedure for generic calls became incomplete, since it could not infer types that were only constrained as part of generic types. For instance, given struct Box<T: Type> { ... } macro unbox<T: type>(box: Box<T>): T the type argument (Smi) at the following call site const box: Box<Smi> = ...; unbox(box); could not be inferred. This CL re-implements the inference procedure and documents the semantics of type argument inference in Torque a bit more clearly. R=tebbi@chromium.org Change-Id: I868f16afbd9864b9c810ac49bc1639b467df939c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1720812 Commit-Queue: Georg Schmid <gsps@google.com> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#63005}
This commit is contained in:
parent
3a1bb751c9
commit
d4e6525849
2
BUILD.gn
2
BUILD.gn
@ -3364,6 +3364,8 @@ v8_source_set("torque_base") {
|
|||||||
"src/torque/torque-compiler.h",
|
"src/torque/torque-compiler.h",
|
||||||
"src/torque/torque-parser.cc",
|
"src/torque/torque-parser.cc",
|
||||||
"src/torque/torque-parser.h",
|
"src/torque/torque-parser.h",
|
||||||
|
"src/torque/type-inference.cc",
|
||||||
|
"src/torque/type-inference.h",
|
||||||
"src/torque/type-oracle.cc",
|
"src/torque/type-oracle.cc",
|
||||||
"src/torque/type-oracle.h",
|
"src/torque/type-oracle.h",
|
||||||
"src/torque/type-visitor.cc",
|
"src/torque/type-visitor.cc",
|
||||||
|
@ -7,6 +7,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/type-inference.h"
|
||||||
#include "src/torque/type-visitor.h"
|
#include "src/torque/type-visitor.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -65,56 +66,20 @@ std::ostream& operator<<(std::ostream& os, const Generic& g) {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
TypeArgumentInference Generic::InferSpecializationTypes(
|
||||||
base::Optional<const Type*> InferTypeArgument(const std::string& to_infer,
|
|
||||||
TypeExpression* parameter,
|
|
||||||
const Type* argument) {
|
|
||||||
BasicTypeExpression* basic = BasicTypeExpression::DynamicCast(parameter);
|
|
||||||
if (basic && basic->namespace_qualification.empty() && !basic->is_constexpr &&
|
|
||||||
basic->name == to_infer) {
|
|
||||||
return argument;
|
|
||||||
}
|
|
||||||
// TODO(gsps): Perform type inference for generic types
|
|
||||||
return base::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::Optional<const Type*> InferTypeArgument(
|
|
||||||
const std::string& to_infer, const std::vector<TypeExpression*>& parameters,
|
|
||||||
const TypeVector& arguments) {
|
|
||||||
for (size_t i = 0; i < arguments.size() && i < parameters.size(); ++i) {
|
|
||||||
if (base::Optional<const Type*> inferred =
|
|
||||||
InferTypeArgument(to_infer, parameters[i], arguments[i])) {
|
|
||||||
return *inferred;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return base::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
base::Optional<TypeVector> Generic::InferSpecializationTypes(
|
|
||||||
const TypeVector& explicit_specialization_types,
|
const TypeVector& explicit_specialization_types,
|
||||||
const TypeVector& arguments) {
|
const TypeVector& arguments) {
|
||||||
TypeVector result = explicit_specialization_types;
|
|
||||||
size_t type_parameter_count = declaration()->generic_parameters.size();
|
|
||||||
if (explicit_specialization_types.size() > type_parameter_count) {
|
|
||||||
return base::nullopt;
|
|
||||||
}
|
|
||||||
for (size_t i = explicit_specialization_types.size();
|
|
||||||
i < type_parameter_count; ++i) {
|
|
||||||
const std::string type_name = declaration()->generic_parameters[i]->value;
|
|
||||||
size_t implicit_count =
|
size_t implicit_count =
|
||||||
declaration()->callable->signature->parameters.implicit_count;
|
declaration()->callable->signature->parameters.implicit_count;
|
||||||
const std::vector<TypeExpression*>& parameters =
|
const std::vector<TypeExpression*>& parameters =
|
||||||
declaration()->callable->signature->parameters.types;
|
declaration()->callable->signature->parameters.types;
|
||||||
std::vector<TypeExpression*> explicit_parameters(
|
std::vector<TypeExpression*> explicit_parameters(
|
||||||
parameters.begin() + implicit_count, parameters.end());
|
parameters.begin() + implicit_count, parameters.end());
|
||||||
base::Optional<const Type*> inferred =
|
|
||||||
InferTypeArgument(type_name, explicit_parameters, arguments);
|
TypeArgumentInference inference(declaration()->generic_parameters,
|
||||||
if (!inferred) return base::nullopt;
|
explicit_specialization_types,
|
||||||
result.push_back(*inferred);
|
explicit_parameters, arguments);
|
||||||
}
|
return inference;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Namespace::IsDefaultNamespace() const {
|
bool Namespace::IsDefaultNamespace() const {
|
||||||
|
@ -21,6 +21,7 @@ namespace torque {
|
|||||||
|
|
||||||
class Scope;
|
class Scope;
|
||||||
class Namespace;
|
class Namespace;
|
||||||
|
class TypeArgumentInference;
|
||||||
|
|
||||||
DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*);
|
DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*);
|
||||||
|
|
||||||
@ -489,7 +490,7 @@ class Generic : public Declarable {
|
|||||||
}
|
}
|
||||||
SpecializationMap<Callable>& specializations() { return specializations_; }
|
SpecializationMap<Callable>& specializations() { return specializations_; }
|
||||||
|
|
||||||
base::Optional<TypeVector> InferSpecializationTypes(
|
TypeArgumentInference InferSpecializationTypes(
|
||||||
const TypeVector& explicit_specialization_types,
|
const TypeVector& explicit_specialization_types,
|
||||||
const TypeVector& arguments);
|
const TypeVector& arguments);
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "src/torque/implementation-visitor.h"
|
#include "src/torque/implementation-visitor.h"
|
||||||
#include "src/torque/parameter-difference.h"
|
#include "src/torque/parameter-difference.h"
|
||||||
#include "src/torque/server-data.h"
|
#include "src/torque/server-data.h"
|
||||||
|
#include "src/torque/type-inference.h"
|
||||||
#include "src/torque/type-visitor.h"
|
#include "src/torque/type-visitor.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -1582,7 +1583,9 @@ namespace {
|
|||||||
void FailCallableLookup(const std::string& reason, const QualifiedName& name,
|
void FailCallableLookup(const std::string& reason, const QualifiedName& name,
|
||||||
const TypeVector& parameter_types,
|
const TypeVector& parameter_types,
|
||||||
const std::vector<Binding<LocalLabel>*>& labels,
|
const std::vector<Binding<LocalLabel>*>& labels,
|
||||||
const std::vector<Signature>& candidates) {
|
const std::vector<Signature>& candidates,
|
||||||
|
const std::vector<std::tuple<Generic*, const char*>>
|
||||||
|
inapplicable_generics) {
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
|
stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
|
||||||
if (labels.size() != 0) {
|
if (labels.size() != 0) {
|
||||||
@ -1596,6 +1599,16 @@ void FailCallableLookup(const std::string& reason, const QualifiedName& name,
|
|||||||
stream << "\n " << name;
|
stream << "\n " << name;
|
||||||
PrintSignature(stream, signature, false);
|
PrintSignature(stream, signature, false);
|
||||||
}
|
}
|
||||||
|
if (inapplicable_generics.size() != 0) {
|
||||||
|
stream << "\nfailed to instantiate all of these generic declarations:";
|
||||||
|
for (auto& failure : inapplicable_generics) {
|
||||||
|
Generic* generic;
|
||||||
|
const char* reason;
|
||||||
|
std::tie(generic, reason) = failure;
|
||||||
|
stream << "\n " << generic->name() << " defined at "
|
||||||
|
<< generic->Position() << ":\n " << reason << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
ReportError(stream.str());
|
ReportError(stream.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1655,17 +1668,20 @@ Callable* ImplementationVisitor::LookupCallable(
|
|||||||
|
|
||||||
std::vector<Declarable*> overloads;
|
std::vector<Declarable*> overloads;
|
||||||
std::vector<Signature> overload_signatures;
|
std::vector<Signature> overload_signatures;
|
||||||
|
std::vector<std::tuple<Generic*, const char*>> inapplicable_generics;
|
||||||
for (auto* declarable : declaration_container) {
|
for (auto* declarable : declaration_container) {
|
||||||
if (Generic* generic = Generic::DynamicCast(declarable)) {
|
if (Generic* generic = Generic::DynamicCast(declarable)) {
|
||||||
base::Optional<TypeVector> inferred_specialization_types =
|
TypeArgumentInference inference = generic->InferSpecializationTypes(
|
||||||
generic->InferSpecializationTypes(specialization_types,
|
specialization_types, parameter_types);
|
||||||
parameter_types);
|
if (inference.HasFailed()) {
|
||||||
if (!inferred_specialization_types) continue;
|
inapplicable_generics.push_back(
|
||||||
|
std::make_tuple(generic, inference.GetFailureReason()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
overloads.push_back(generic);
|
overloads.push_back(generic);
|
||||||
overload_signatures.push_back(
|
overload_signatures.push_back(
|
||||||
DeclarationVisitor::MakeSpecializedSignature(
|
DeclarationVisitor::MakeSpecializedSignature(
|
||||||
SpecializationKey<Generic>{generic,
|
SpecializationKey<Generic>{generic, inference.GetResult()}));
|
||||||
*inferred_specialization_types}));
|
|
||||||
} else if (Callable* callable = Callable::DynamicCast(declarable)) {
|
} else if (Callable* callable = Callable::DynamicCast(declarable)) {
|
||||||
overloads.push_back(callable);
|
overloads.push_back(callable);
|
||||||
overload_signatures.push_back(callable->signature());
|
overload_signatures.push_back(callable->signature());
|
||||||
@ -1684,7 +1700,7 @@ Callable* ImplementationVisitor::LookupCallable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overloads.empty()) {
|
if (overloads.empty() && inapplicable_generics.empty()) {
|
||||||
if (silence_errors) return nullptr;
|
if (silence_errors) return nullptr;
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
stream << "no matching declaration found for " << name;
|
stream << "no matching declaration found for " << name;
|
||||||
@ -1692,7 +1708,8 @@ Callable* ImplementationVisitor::LookupCallable(
|
|||||||
} else if (candidates.empty()) {
|
} else if (candidates.empty()) {
|
||||||
if (silence_errors) return nullptr;
|
if (silence_errors) return nullptr;
|
||||||
FailCallableLookup("cannot find suitable callable with name", name,
|
FailCallableLookup("cannot find suitable callable with name", name,
|
||||||
parameter_types, labels, overload_signatures);
|
parameter_types, labels, overload_signatures,
|
||||||
|
inapplicable_generics);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto is_better_candidate = [&](size_t a, size_t b) {
|
auto is_better_candidate = [&](size_t a, size_t b) {
|
||||||
@ -1713,14 +1730,15 @@ Callable* ImplementationVisitor::LookupCallable(
|
|||||||
candidate_signatures.push_back(overload_signatures[i]);
|
candidate_signatures.push_back(overload_signatures[i]);
|
||||||
}
|
}
|
||||||
FailCallableLookup("ambiguous callable ", name, parameter_types, labels,
|
FailCallableLookup("ambiguous callable ", name, parameter_types, labels,
|
||||||
candidate_signatures);
|
candidate_signatures, inapplicable_generics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Generic* generic = Generic::DynamicCast(overloads[best])) {
|
if (Generic* generic = Generic::DynamicCast(overloads[best])) {
|
||||||
result = GetOrCreateSpecialization(SpecializationKey<Generic>{
|
TypeArgumentInference inference = generic->InferSpecializationTypes(
|
||||||
generic, *generic->InferSpecializationTypes(specialization_types,
|
specialization_types, parameter_types);
|
||||||
parameter_types)});
|
result = GetOrCreateSpecialization(
|
||||||
|
SpecializationKey<Generic>{generic, inference.GetResult()});
|
||||||
} else {
|
} else {
|
||||||
result = Callable::cast(overloads[best]);
|
result = Callable::cast(overloads[best]);
|
||||||
}
|
}
|
||||||
|
118
src/torque/type-inference.cc
Normal file
118
src/torque/type-inference.cc
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "src/torque/type-inference.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
namespace torque {
|
||||||
|
|
||||||
|
TypeArgumentInference::TypeArgumentInference(
|
||||||
|
const NameVector& type_parameters,
|
||||||
|
const TypeVector& explicit_type_arguments,
|
||||||
|
const std::vector<TypeExpression*> term_parameters,
|
||||||
|
const TypeVector& term_argument_types)
|
||||||
|
: num_explicit_(explicit_type_arguments.size()),
|
||||||
|
type_parameter_from_name_(type_parameters.size()),
|
||||||
|
inferred_(type_parameters.size()) {
|
||||||
|
if (num_explicit_ > type_parameters.size()) {
|
||||||
|
Fail("more explicit type arguments than expected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < type_parameters.size(); i++) {
|
||||||
|
type_parameter_from_name_[type_parameters[i]->value] = i;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < num_explicit_; i++) {
|
||||||
|
inferred_[i] = {explicit_type_arguments[i]};
|
||||||
|
}
|
||||||
|
|
||||||
|
DCHECK_EQ(term_parameters.size(), term_argument_types.size());
|
||||||
|
for (size_t i = 0; i < term_parameters.size(); i++) {
|
||||||
|
Match(term_parameters[i], term_argument_types[i]);
|
||||||
|
if (HasFailed()) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < type_parameters.size(); i++) {
|
||||||
|
if (!inferred_[i]) {
|
||||||
|
Fail("failed to infer arguments for all type parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeVector TypeArgumentInference::GetResult() const {
|
||||||
|
CHECK(!HasFailed());
|
||||||
|
TypeVector result(inferred_.size());
|
||||||
|
std::transform(
|
||||||
|
inferred_.begin(), inferred_.end(), result.begin(),
|
||||||
|
[](base::Optional<const Type*> maybe_type) { return *maybe_type; });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeArgumentInference::Match(TypeExpression* parameter,
|
||||||
|
const Type* argument_type) {
|
||||||
|
if (BasicTypeExpression* basic =
|
||||||
|
BasicTypeExpression::DynamicCast(parameter)) {
|
||||||
|
// If the parameter is referring to one of the type parameters, substitute
|
||||||
|
if (basic->namespace_qualification.empty() && !basic->is_constexpr) {
|
||||||
|
auto result = type_parameter_from_name_.find(basic->name);
|
||||||
|
if (result != type_parameter_from_name_.end()) {
|
||||||
|
size_t type_parameter_index = result->second;
|
||||||
|
if (type_parameter_index < num_explicit_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
base::Optional<const Type*>& maybe_inferred =
|
||||||
|
inferred_[type_parameter_index];
|
||||||
|
if (maybe_inferred && *maybe_inferred != argument_type) {
|
||||||
|
Fail("found conflicting types for generic parameter");
|
||||||
|
} else {
|
||||||
|
inferred_[type_parameter_index] = {argument_type};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try to recurse in case of generic types
|
||||||
|
if (!basic->generic_arguments.empty()) {
|
||||||
|
auto* argument_struct_type = StructType::DynamicCast(argument_type);
|
||||||
|
if (argument_struct_type) {
|
||||||
|
MatchGeneric(basic, argument_struct_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NOTE: We could also check whether ground parameter types match the
|
||||||
|
// argument types, but we are only interested in inferring type arguments
|
||||||
|
// here
|
||||||
|
} else {
|
||||||
|
Fail("unsupported parameter expression");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeArgumentInference::MatchGeneric(BasicTypeExpression* parameter,
|
||||||
|
const StructType* argument_type) {
|
||||||
|
QualifiedName qualified_name{parameter->namespace_qualification,
|
||||||
|
parameter->name};
|
||||||
|
GenericStructType* generic_struct =
|
||||||
|
Declarations::LookupUniqueGenericStructType(qualified_name);
|
||||||
|
auto& specialized_from = argument_type->GetSpecializedFrom();
|
||||||
|
if (!specialized_from || specialized_from->generic != generic_struct) {
|
||||||
|
return Fail("found conflicting generic type constructors");
|
||||||
|
}
|
||||||
|
auto& parameters = parameter->generic_arguments;
|
||||||
|
auto& argument_types = specialized_from->specialized_types;
|
||||||
|
if (parameters.size() != argument_types.size()) {
|
||||||
|
Error(
|
||||||
|
"cannot infer types from generic-struct-typed parameter with "
|
||||||
|
"incompatible number of arguments")
|
||||||
|
.Position(parameter->pos)
|
||||||
|
.Throw();
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < parameters.size(); i++) {
|
||||||
|
Match(parameters[i], argument_types[i]);
|
||||||
|
if (HasFailed()) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace torque
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
78
src/torque/type-inference.h
Normal file
78
src/torque/type-inference.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2019 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef V8_TORQUE_TYPE_INFERENCE_H_
|
||||||
|
#define V8_TORQUE_TYPE_INFERENCE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "src/base/optional.h"
|
||||||
|
#include "src/torque/ast.h"
|
||||||
|
#include "src/torque/declarations.h"
|
||||||
|
#include "src/torque/types.h"
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
namespace internal {
|
||||||
|
namespace torque {
|
||||||
|
|
||||||
|
// Type argument inference computes a potential instantiation of a generic
|
||||||
|
// callable given some concrete argument types. As an example, consider the
|
||||||
|
// generic macro
|
||||||
|
//
|
||||||
|
// macro Pick<T: type>(x: T, y: T): T
|
||||||
|
//
|
||||||
|
// along with a given call site, such as
|
||||||
|
//
|
||||||
|
// Pick(1, 2);
|
||||||
|
//
|
||||||
|
// The inference proceeds by matching the term argument types (`constexpr
|
||||||
|
// int31`, in case of `1` and `2`) against the formal parameter types (`T` in
|
||||||
|
// both cases). During this matching we discover that `T` must equal `constexpr
|
||||||
|
// int31`.
|
||||||
|
//
|
||||||
|
// The inference will not perform any comprehensive type checking of its own,
|
||||||
|
// but *does* fail if type parameters cannot be soundly instantiated given the
|
||||||
|
// call site. For instance, for the following call site
|
||||||
|
//
|
||||||
|
// const aSmi: Smi = ...;
|
||||||
|
// Pick(1, aSmi); // inference fails
|
||||||
|
//
|
||||||
|
// inference would fail, since `constexpr int31` is distinct from `Smi`. To
|
||||||
|
// allow for implicit conversions to be tried in a separate step after type
|
||||||
|
// argument inference, a number of type arguments may be given explicitly:
|
||||||
|
//
|
||||||
|
// Pick<Smi>(1, aSmi); // inference succeeds (doing nothing)
|
||||||
|
//
|
||||||
|
// In the above case the inference simply ignores inconsistent constraints on
|
||||||
|
// `T`.
|
||||||
|
class TypeArgumentInference {
|
||||||
|
public:
|
||||||
|
TypeArgumentInference(const NameVector& type_parameters,
|
||||||
|
const TypeVector& explicit_type_arguments,
|
||||||
|
const std::vector<TypeExpression*> term_parameters,
|
||||||
|
const TypeVector& term_argument_types);
|
||||||
|
|
||||||
|
bool HasFailed() const { return failure_reason_.has_value(); }
|
||||||
|
const char* GetFailureReason() { return *failure_reason_; }
|
||||||
|
TypeVector GetResult() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Fail(const char* reason) { failure_reason_ = {reason}; }
|
||||||
|
|
||||||
|
void Match(TypeExpression* parameter, const Type* argument_type);
|
||||||
|
void MatchGeneric(BasicTypeExpression* parameter,
|
||||||
|
const StructType* argument_type);
|
||||||
|
|
||||||
|
size_t num_explicit_;
|
||||||
|
std::unordered_map<std::string, size_t> type_parameter_from_name_;
|
||||||
|
std::vector<base::Optional<const Type*>> inferred_;
|
||||||
|
base::Optional<const char*> failure_reason_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace torque
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // V8_TORQUE_TYPE_INFERENCE_H_
|
@ -475,6 +475,9 @@ class StructType final : public AggregateType {
|
|||||||
std::string ToExplicitString() const override;
|
std::string ToExplicitString() const override;
|
||||||
std::string GetGeneratedTypeNameImpl() const override;
|
std::string GetGeneratedTypeNameImpl() const override;
|
||||||
std::string MangledName() const override;
|
std::string MangledName() const override;
|
||||||
|
const MaybeSpecializationKey& GetSpecializedFrom() const {
|
||||||
|
return specialized_from_;
|
||||||
|
}
|
||||||
|
|
||||||
static base::Optional<const Type*> MatchUnaryGeneric(
|
static base::Optional<const Type*> MatchUnaryGeneric(
|
||||||
const Type* type, GenericStructType* generic);
|
const Type* type, GenericStructType* generic);
|
||||||
|
@ -898,8 +898,7 @@ namespace test {
|
|||||||
const ref:&Smi = & array.a;
|
const ref:&Smi = & array.a;
|
||||||
* ref = 3 + * ref;
|
* ref = 3 + * ref;
|
||||||
-- * ref;
|
-- * ref;
|
||||||
// TODO(gsps): Remove explicit type arg once inference works
|
Swap(& array.b, array.GetA());
|
||||||
Swap<Smi>(& array.b, array.GetA());
|
|
||||||
check(array.a == 2);
|
check(array.a == 2);
|
||||||
check(array.b == 9);
|
check(array.b == 9);
|
||||||
}
|
}
|
||||||
@ -1002,7 +1001,7 @@ namespace test {
|
|||||||
@export
|
@export
|
||||||
macro TestGenericStruct2(): TestTuple<Smi, intptr> {
|
macro TestGenericStruct2(): TestTuple<Smi, intptr> {
|
||||||
const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2};
|
const intptrAndSmi = TestTuple<intptr, Smi>{fst: 1, snd: 2};
|
||||||
const smiAndIntptr = TupleSwap<intptr, Smi>(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;
|
return smiAndIntptr;
|
||||||
|
Loading…
Reference in New Issue
Block a user