diff --git a/explorer/ast/BUILD b/explorer/ast/BUILD index 689b81a25507..10f5250c794d 100644 --- a/explorer/ast/BUILD +++ b/explorer/ast/BUILD @@ -37,6 +37,7 @@ cc_library( cc_library( name = "ast", srcs = [ + "bindings.cpp", "declaration.cpp", "expression.cpp", "member.cpp", @@ -45,6 +46,7 @@ cc_library( ], hdrs = [ "ast.h", + "bindings.h", "declaration.h", "expression.h", "impl_binding.h", @@ -55,12 +57,12 @@ cc_library( ], deps = [ ":ast_node", - ":bindings", ":library_name", ":paren_contents", ":static_scope", ":value_category", "//common:check", + "//common:error", "//common:indirect_value", "//common:ostream", "//explorer/common:arena", @@ -99,15 +101,6 @@ cc_test( ], ) -cc_library( - name = "bindings", - srcs = ["bindings.cpp"], - hdrs = ["bindings.h"], - deps = [ - "//explorer/common:nonnull", - ], -) - cc_test( name = "expression_test", srcs = ["expression_test.cpp"], diff --git a/explorer/ast/ast_rtti.txt b/explorer/ast/ast_rtti.txt index 1794faea9fa6..52e4dc42996a 100644 --- a/explorer/ast/ast_rtti.txt +++ b/explorer/ast/ast_rtti.txt @@ -50,8 +50,9 @@ abstract class Expression : AstNode; class BoolLiteral : Expression; class CallExpression : Expression; class FunctionTypeLiteral : Expression; - class SimpleMemberAccessExpression : Expression; - class CompoundMemberAccessExpression : Expression; + abstract class MemberAccessExpression : Expression; + class SimpleMemberAccessExpression : MemberAccessExpression; + class CompoundMemberAccessExpression : MemberAccessExpression; class IndexExpression : Expression; class IntTypeLiteral : Expression; class ContinuationTypeLiteral : Expression; diff --git a/explorer/ast/bindings.cpp b/explorer/ast/bindings.cpp index 2458331e7a4d..db2f795243a9 100644 --- a/explorer/ast/bindings.cpp +++ b/explorer/ast/bindings.cpp @@ -4,11 +4,46 @@ #include "explorer/ast/bindings.h" +#include "common/error.h" +#include "explorer/ast/impl_binding.h" +#include "explorer/ast/pattern.h" + namespace Carbon { +void Bindings::Add(Nonnull binding, + Nonnull value, + std::optional> witness) { + bool added_value = args_.insert({binding, value}).second; + CARBON_CHECK(added_value) << "Add of already-existing binding"; + + if (witness) { + // TODO: Eventually we should check that we have a witness if and only if + // the binding has an impl binding. + auto impl_binding = binding->impl_binding(); + CARBON_CHECK(impl_binding) << "Given witness but have no impl binding"; + bool added_witness = witnesses_.insert({*impl_binding, *witness}).second; + CARBON_CHECK(added_witness) << "Add of already-existing binding"; + } +} + auto Bindings::None() -> Nonnull { - static Nonnull bindings = new Bindings({}, {}); + static Nonnull bindings = new Bindings; return bindings; } +auto Bindings::SymbolicIdentity( + Nonnull arena, + llvm::ArrayRef> bindings) + -> Nonnull { + auto* result = arena->New(); + for (auto* binding : bindings) { + std::optional> witness; + if (binding->impl_binding()) { + witness = *binding->impl_binding().value()->symbolic_identity(); + } + result->Add(binding, *binding->symbolic_identity(), witness); + } + return result; +} + } // namespace Carbon diff --git a/explorer/ast/bindings.h b/explorer/ast/bindings.h index 1fe517685e72..61f72be46414 100644 --- a/explorer/ast/bindings.h +++ b/explorer/ast/bindings.h @@ -8,9 +8,11 @@ #include #include "explorer/common/nonnull.h" +#include "llvm/ADT/ArrayRef.h" namespace Carbon { +class Arena; class ImplBinding; class GenericBinding; class Value; @@ -27,6 +29,19 @@ using ImplWitnessMap = // will have the same set of bindings. class Bindings { public: + // Gets an empty set of bindings. + static auto None() -> Nonnull; + + // Makes a set of symbolic identity bindings for the given collection of + // generic bindings and their impl bindings. + static auto SymbolicIdentity( + Nonnull arena, + llvm::ArrayRef> bindings) + -> Nonnull; + + // Create an empty set of bindings. + Bindings() {} + // Create an instantiated set of bindings for use during evaluation, // containing both arguments and witnesses. Bindings(BindingMap args, ImplWitnessMap witnesses) @@ -38,15 +53,16 @@ class Bindings { // arguments but not the corresponding witnesses. Bindings(BindingMap args, NoWitnessesTag) : args_(args), witnesses_() {} + // Add a value, and perhaps a witness, for a generic binding. + void Add(Nonnull binding, Nonnull value, + std::optional> witness); + // Argument values corresponding to generic bindings. auto args() const -> const BindingMap& { return args_; } // Witnesses corresponding to impl bindings. auto witnesses() const -> const ImplWitnessMap& { return witnesses_; } - // An empty set of bindings. - static auto None() -> Nonnull; - // Determine whether this is an empty set of bindings. [[nodiscard]] auto empty() const -> bool { return args_.empty() && witnesses_.empty(); diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index 611d92c53ec7..e80d1924c5e7 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -205,21 +205,66 @@ class DotSelfExpression : public Expression { std::optional> self_binding_; }; -class SimpleMemberAccessExpression : public Expression { +class MemberAccessExpression : public Expression { + public: + explicit MemberAccessExpression(AstNodeKind kind, SourceLocation source_loc, + Nonnull object) + : Expression(kind, source_loc), object_(object) {} + + static auto classof(const AstNode* node) -> bool { + return InheritsFromMemberAccessExpression(node->kind()); + } + + auto object() const -> const Expression& { return *object_; } + auto object() -> Expression& { return *object_; } + + // Can only be called by type-checking, if a conversion was required. + void set_object(Nonnull object) { object_ = object; } + + // Returns true if this is an access of a member of the type of the object, + // rather than an access of a member of the object itself. In this case, the + // value of the object expression is ignored, and the type is accessed + // instead. + // + // For example, given `x: Class`, `x.StaticFunction` is a type access + // equivalent to `T.StaticFunction`, and given `T:! Interface` and `y: T`, + // `y.AssociatedConstant` is a type access equivalent to + // `T.AssociatedConstant`. + auto is_type_access() const -> bool { return is_type_access_; } + + // Can only be called once, during typechecking. + void set_is_type_access(bool type_access) { is_type_access_ = type_access; } + + // If `object` has a generic type, returns the witness value, which might be + // either concrete or symbolic. Otherwise, returns `std::nullopt`. Should not + // be called before typechecking. + auto impl() const -> std::optional> { return impl_; } + + // Can only be called once, during typechecking. + void set_impl(Nonnull impl) { + CARBON_CHECK(!impl_.has_value()); + impl_ = impl; + } + + private: + Nonnull object_; + bool is_type_access_ = false; + std::optional> impl_; +}; + +class SimpleMemberAccessExpression : public MemberAccessExpression { public: explicit SimpleMemberAccessExpression(SourceLocation source_loc, Nonnull object, std::string member_name) - : Expression(AstNodeKind::SimpleMemberAccessExpression, source_loc), - object_(object), + : MemberAccessExpression(AstNodeKind::SimpleMemberAccessExpression, + source_loc, object), member_name_(std::move(member_name)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromSimpleMemberAccessExpression(node->kind()); } - auto object() const -> const Expression& { return *object_; } - auto object() -> Expression& { return *object_; } auto member_name() const -> const std::string& { return member_name_; } // Returns the `Member` that the member name resolved to. @@ -237,6 +282,7 @@ class SimpleMemberAccessExpression : public Expression { // Returns true if the field is a method that has a "me" declaration in an // AddrPattern. + // TODO: Should be in MemberAccessExpression. auto is_field_addr_me_method() const -> bool { return is_field_addr_me_method_; } @@ -244,17 +290,6 @@ class SimpleMemberAccessExpression : public Expression { // Can only be called once, during typechecking. void set_is_field_addr_me_method() { is_field_addr_me_method_ = true; } - // If `object` has a generic type, returns the witness value, which might be - // either concrete or symbolic. Otherwise, returns `std::nullopt`. Should not - // be called before typechecking. - auto impl() const -> std::optional> { return impl_; } - - // Can only be called once, during typechecking. - void set_impl(Nonnull impl) { - CARBON_CHECK(!impl_.has_value()); - impl_ = impl; - } - // If `object` is a constrained type parameter and `member` was found in an // interface, returns that interface. Should not be called before // typechecking. @@ -270,11 +305,9 @@ class SimpleMemberAccessExpression : public Expression { } private: - Nonnull object_; std::string member_name_; std::optional member_; bool is_field_addr_me_method_ = false; - std::optional> impl_; std::optional> found_in_interface_; }; @@ -291,21 +324,19 @@ class SimpleMemberAccessExpression : public Expression { // // Note that the `path` is evaluated during type-checking, not at runtime, so // the corresponding `member` is determined statically. -class CompoundMemberAccessExpression : public Expression { +class CompoundMemberAccessExpression : public MemberAccessExpression { public: explicit CompoundMemberAccessExpression(SourceLocation source_loc, Nonnull object, Nonnull path) - : Expression(AstNodeKind::CompoundMemberAccessExpression, source_loc), - object_(object), + : MemberAccessExpression(AstNodeKind::CompoundMemberAccessExpression, + source_loc, object), path_(path) {} static auto classof(const AstNode* node) -> bool { return InheritsFromCompoundMemberAccessExpression(node->kind()); } - auto object() const -> const Expression& { return *object_; } - auto object() -> Expression& { return *object_; } auto path() const -> const Expression& { return *path_; } auto path() -> Expression& { return *path_; } @@ -322,24 +353,9 @@ class CompoundMemberAccessExpression : public Expression { member_ = member; } - // If this expression names an interface member, returns the witness value, - // which might be symbolic. - auto impl() const -> std::optional> { return impl_; } - - // Can only be called once, during typechecking. - void set_impl(Nonnull impl) { - CARBON_CHECK(!impl_.has_value()); - impl_ = impl; - } - - // Can only be called by type-checking, if a conversion was required. - void set_object(Nonnull object) { object_ = object; } - private: - Nonnull object_; Nonnull path_; std::optional> member_; - std::optional> impl_; }; class IndexExpression : public Expression { diff --git a/explorer/ast/impl_binding.h b/explorer/ast/impl_binding.h index abeb70787fca..5c0db67515d4 100644 --- a/explorer/ast/impl_binding.h +++ b/explorer/ast/impl_binding.h @@ -32,7 +32,7 @@ class ImplBinding : public AstNode { ImplBinding(SourceLocation source_loc, Nonnull type_var, - Nonnull iface) + std::optional> iface) : AstNode(AstNodeKind::ImplBinding, source_loc), type_var_(type_var), iface_(iface) {} @@ -45,8 +45,19 @@ class ImplBinding : public AstNode { // The binding for the type variable. auto type_var() const -> Nonnull { return type_var_; } - // The interface being implemented. - auto interface() const -> Nonnull { return iface_; } + // The constraint being implemented. + // TODO: Rename this to `constraint`. + auto interface() const -> Nonnull { + CARBON_CHECK(iface_) << "interface has not been set yet"; + return *iface_; + } + + // Set the interface being implemented, if not set by the constructor. Should + // only be called by typechecking. + void set_interface(Nonnull iface) { + CARBON_CHECK(!iface_) << "interface set twice"; + iface_ = iface; + } // Required for the ValueNode interface auto constant_value() const -> std::optional> { @@ -60,14 +71,10 @@ class ImplBinding : public AstNode { symbolic_identity_ = value; } - // The static type of the impl. Cannot be called before typechecking. - auto static_type() const -> const Value& { return **static_type_; } - - // Sets the static type of the impl. Can only be called once, during - // typechecking. - void set_static_type(Nonnull type) { - CARBON_CHECK(!static_type_.has_value()); - static_type_ = type; + // These functions exist only so that an `ImplBinding` can be used as a + // `ValueNodeView` as a key in a `StaticScope`. + auto static_type() const -> const Value& { + CARBON_FATAL() << "an ImplBinding has no type"; } auto value_category() const -> ValueCategory { return ValueCategory::Let; } @@ -84,9 +91,8 @@ class ImplBinding : public AstNode { private: Nonnull type_var_; - Nonnull iface_; + std::optional> iface_; std::optional> symbolic_identity_; - std::optional> static_type_; std::optional> original_; }; diff --git a/explorer/interpreter/BUILD b/explorer/interpreter/BUILD index 1ee8df729568..3b40bd74d06c 100644 --- a/explorer/interpreter/BUILD +++ b/explorer/interpreter/BUILD @@ -25,7 +25,6 @@ cc_library( "//common:check", "//common:ostream", "//explorer/ast", - "//explorer/ast:bindings", "//explorer/common:arena", "//explorer/common:error_builders", "//explorer/common:nonnull", diff --git a/explorer/interpreter/impl_scope.cpp b/explorer/interpreter/impl_scope.cpp index 7b4847286d03..e75a1629604a 100644 --- a/explorer/interpreter/impl_scope.cpp +++ b/explorer/interpreter/impl_scope.cpp @@ -26,16 +26,10 @@ void ImplScope::Add(Nonnull iface, llvm::ArrayRef> impl_bindings, Nonnull witness, const TypeChecker& type_checker) { - if (auto* orig_constraint = dyn_cast(iface)) { - BindingMap map; - map[orig_constraint->self_binding()] = type; - const ConstraintType* constraint = - cast(type_checker.Substitute(map, orig_constraint)); - for (size_t i = 0; i != constraint->impl_constraints().size(); ++i) { - ConstraintType::ImplConstraint impl = constraint->impl_constraints()[i]; - Add(impl.interface, deduced, impl.type, impl_bindings, - type_checker.MakeConstraintWitnessAccess(witness, i), type_checker); - } + if (auto* constraint = dyn_cast(iface)) { + // The caller should have substituted `.Self` for `type` already. + Add(constraint->impl_constraints(), deduced, impl_bindings, witness, + type_checker); // A parameterized impl declaration doesn't contribute any equality // constraints to the scope. Instead, we'll resolve the equality // constraints by resolving a witness when needed. @@ -54,6 +48,18 @@ void ImplScope::Add(Nonnull iface, .witness = witness}); } +void ImplScope::Add(llvm::ArrayRef impls, + llvm::ArrayRef> deduced, + llvm::ArrayRef> impl_bindings, + Nonnull witness, + const TypeChecker& type_checker) { + for (size_t i = 0; i != impls.size(); ++i) { + ConstraintType::ImplConstraint impl = impls[i]; + Add(impl.interface, deduced, impl.type, impl_bindings, + type_checker.MakeConstraintWitnessAccess(witness, i), type_checker); + } +} + void ImplScope::AddParent(Nonnull parent) { parent_scopes_.push_back(parent); } @@ -68,15 +74,29 @@ auto ImplScope::Resolve(Nonnull constraint_type, } if (const auto* constraint = dyn_cast(constraint_type)) { std::vector> witnesses; - BindingMap map; - map[constraint->self_binding()] = impl_type; for (auto impl : constraint->impl_constraints()) { + // Note that later impl constraints can refer to earlier impl constraints + // via impl bindings. For example, in + // `C where .Self.AssocType is D`, + // ... the `.Self.AssocType is D` constraint refers to the `.Self is C` + // constraint when naming `AssocType`. So incrementally build up a + // partial constraint witness as we go. + std::optional> witness; + if (constraint->self_binding()->impl_binding()) { + // Note, this is a partial impl binding covering only the impl + // constraints that we've already seen. Earlier impl constraints should + // not be able to refer to impl bindings for later impl constraints. + witness = type_checker.MakeConstraintWitness(*constraint, witnesses, + source_loc); + } + Bindings bindings; + bindings.Add(constraint->self_binding(), impl_type, witness); CARBON_ASSIGN_OR_RETURN( Nonnull result, - ResolveInterface( - cast(type_checker.Substitute(map, impl.interface)), - type_checker.Substitute(map, impl.type), source_loc, - type_checker)); + ResolveInterface(cast(type_checker.Substitute( + bindings, impl.interface)), + type_checker.Substitute(bindings, impl.type), + source_loc, type_checker)); witnesses.push_back(result); } // TODO: Check satisfaction of same-type constraints. diff --git a/explorer/interpreter/impl_scope.h b/explorer/interpreter/impl_scope.h index cb80b6b7189b..5f23d1a2aed0 100644 --- a/explorer/interpreter/impl_scope.h +++ b/explorer/interpreter/impl_scope.h @@ -42,23 +42,34 @@ class TypeChecker; // scope. class ImplScope { public: - // Associates `iface` and `type` with the `impl` in this scope. + // Associates `iface` and `type` with the `impl` in this scope. If `iface` is + // a constraint type, it will be split into its constituent components, and + // any references to `.Self` are expected to have been substituted for the + // type implementing the constraint. void Add(Nonnull iface, Nonnull type, Nonnull witness, const TypeChecker& type_checker); // For a parameterized impl, associates `iface` and `type` - // with the `impl` in this scope. + // with the `impl` in this scope. Otherwise, the same as the previous + // overload. void Add(Nonnull iface, llvm::ArrayRef> deduced, Nonnull type, llvm::ArrayRef> impl_bindings, Nonnull witness, const TypeChecker& type_checker); + // Adds a list of impl constraints from a constraint type into scope. Any + // references to `.Self` are expected to have already been substituted for + // the type implementing the constraint. + void Add(llvm::ArrayRef impls, + llvm::ArrayRef> deduced, + llvm::ArrayRef> impl_bindings, + Nonnull witness, const TypeChecker& type_checker); - // Add a type equality constraint. + // Adds a type equality constraint. void AddEqualityConstraint(Nonnull equal) { equalities_.push_back(equal); } - // Make `parent` a parent of this scope. + // Makes `parent` a parent of this scope. // REQUIRES: `parent` is not already a parent of this scope. void AddParent(Nonnull parent); diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index d33e0f12c71f..80cee73db03d 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -540,27 +540,21 @@ auto Interpreter::EvalAssociatedConstant( auto& impl_witness = cast(*witness); Nonnull constraint = impl_witness.declaration().constraint_type(); - Nonnull expected = arena_->New( - &constraint->self_binding()->value(), &assoc->interface(), - &assoc->constant(), &impl_witness); std::optional> result; - constraint->VisitEqualValues(expected, - [&](Nonnull equal_value) { - // TODO: The value might depend on the - // parameters of the impl. We need to - // substitute impl_witness.type_args() into the - // value. - if (isa(equal_value)) { - return true; - } - // TODO: This makes an arbitrary choice if - // there's more than one equal value. It's not - // clear how to handle that case. - result = equal_value; - return false; - }); + constraint->VisitEqualValues(assoc, [&](Nonnull equal_value) { + // TODO: The value might depend on the parameters of the impl. We need to + // substitute impl_witness.type_args() into the value or constraint. + if (isa(equal_value)) { + return true; + } + // TODO: This makes an arbitrary choice if there's more than one equal + // value. It's not clear how to handle that case. + result = equal_value; + return false; + }); if (!result) { - CARBON_FATAL() << impl_witness.declaration() + CARBON_FATAL() << impl_witness.declaration() << " with constraint " + << *constraint << " is missing value for associated constant " << *assoc; } return *result; @@ -1013,7 +1007,11 @@ auto Interpreter::StepExp() -> ErrorOr { FieldPath::Component member(access.member(), found_in_interface, witness); const Value* aggregate; - if (const auto* lvalue = dyn_cast(act.results()[0])) { + if (access.is_type_access()) { + CARBON_ASSIGN_OR_RETURN( + aggregate, InstantiateType(&access.object().static_type(), + access.source_loc())); + } else if (const auto* lvalue = dyn_cast(act.results()[0])) { CARBON_ASSIGN_OR_RETURN( aggregate, this->heap_.Read(lvalue->address(), exp.source_loc())); @@ -1066,6 +1064,11 @@ auto Interpreter::StepExp() -> ErrorOr { } else { // Access the object to find the named member. Nonnull object = act.results()[0]; + if (access.is_type_access()) { + CARBON_ASSIGN_OR_RETURN( + object, InstantiateType(&access.object().static_type(), + access.source_loc())); + } std::optional> witness; if (access.impl().has_value()) { witness = cast(act.results()[1]); @@ -1359,9 +1362,8 @@ auto Interpreter::StepExp() -> ErrorOr { } else { // { { rt :: fn pt -> [] :: C, E, F} :: S, H} // -> { fn pt -> rt :: {C, E, F} :: S, H} - return todo_.FinishAction(arena_->New( - act.results()[0], llvm::None, act.results()[1], llvm::None, - llvm::None)); + return todo_.FinishAction( + arena_->New(act.results()[0], act.results()[1])); } } case ExpressionKind::ContinuationTypeLiteral: { diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 7a1fe96aaa95..dcbdff636e78 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -46,6 +46,11 @@ struct TypeChecker::SingleStepEqualityContext : public EqualityContext { if (impl_witness) { return impl_witness; } + if (type_checker_->trace_stream_) { + **type_checker_->trace_stream_ << "found symbolic witness " + << assoc->witness() + << "; performing impl scope lookup\n"; + } CARBON_ASSIGN_OR_RETURN( Nonnull witness, @@ -88,10 +93,13 @@ struct TypeChecker::SingleStepEqualityContext : public EqualityContext { // Instantiate the impl to find the concrete constraint it implements. Nonnull constraint = (*impl_witness)->declaration().constraint_type(); - BindingMap bindings = (*impl_witness)->type_args(); - bindings[constraint->self_binding()] = &assoc->base(); constraint = cast( - type_checker_->Substitute(bindings, constraint)); + type_checker_->Substitute((*impl_witness)->bindings(), constraint)); + if (type_checker_->trace_stream_) { + **type_checker_->trace_stream_ << "found constraint " << *constraint + << " for associated constant " + << *assoc << "\n"; + } // Look for the value of this constant within that constraint. if (!constraint->VisitEqualValues(value, visitor)) { @@ -373,7 +381,7 @@ auto TypeChecker::FieldTypes(const NominalClassType& class_type) const case DeclarationKind::VariableDeclaration: { const auto& var = cast(*m); Nonnull field_type = - Substitute(class_type.type_args(), &var.binding().static_type()); + Substitute(class_type.bindings(), &var.binding().static_type()); field_types.push_back( {.name = var.binding().name(), .value = field_type}); break; @@ -657,7 +665,9 @@ auto TypeChecker::ArgumentDeduction( << "expected: " << *param << "\n" << "actual: " << *arg; } - const Value* subst_param_type = Substitute(deduced, param); + // TODO: Compute or deduce witnesses. + Bindings bindings(deduced, Bindings::NoWitnesses); + const Value* subst_param_type = Substitute(bindings, param); return allow_implicit_conversion ? ExpectType(source_loc, context, subst_param_type, arg, impl_scope) @@ -670,7 +680,10 @@ auto TypeChecker::ArgumentDeduction( const auto& var_type = cast(*param); const auto& binding = cast(*param).binding(); if (binding.has_static_type()) { - const Value* binding_type = Substitute(deduced, &binding.static_type()); + // TODO: Compute or deduce witnesses. + Bindings bindings(deduced, Bindings::NoWitnesses); + const Value* binding_type = + Substitute(bindings, &binding.static_type()); if (!IsTypeOfType(binding_type)) { if (!IsImplicitlyConvertible(arg, binding_type, impl_scope, false)) { return ProgramError(source_loc) @@ -912,27 +925,38 @@ auto TypeChecker::ArgumentDeduction( class ConstraintTypeBuilder { public: ConstraintTypeBuilder(Nonnull arena, SourceLocation source_loc) - : self_binding_(MakeSelfBinding(arena, source_loc)) {} - ConstraintTypeBuilder(Nonnull self_binding) - : self_binding_(self_binding) {} + : ConstraintTypeBuilder(arena, MakeSelfBinding(arena, source_loc)) {} + ConstraintTypeBuilder(Nonnull arena, + Nonnull self_binding) + : self_binding_(PrepareSelfBinding(arena, self_binding)), + impl_binding_(AddImplBinding(arena, self_binding_)) {} - // Produce a type that refers to the `.Self` type of the constraint. - auto GetSelfType(Nonnull arena) const -> Nonnull { + // Produces a type that refers to the `.Self` type of the constraint. + auto GetSelfType() const -> Nonnull { return &self_binding_->value(); } - // Add an `impl` constraint -- `T is C` if not already present. - void AddImplConstraint(ConstraintType::ImplConstraint impl) { - for (ConstraintType::ImplConstraint existing : impl_constraints_) { + // Gets a witness that `.Self` implements the eventual constraint type built + // by this builder. + auto GetSelfWitness() const -> Nonnull { + return cast(impl_binding_->symbolic_identity().value()); + } + + // Adds an `impl` constraint -- `T is C` if not already present. + // Returns the index of the impl constraint within the self witness. + auto AddImplConstraint(ConstraintType::ImplConstraint impl) -> int { + for (int i = 0; i != static_cast(impl_constraints_.size()); ++i) { + ConstraintType::ImplConstraint& existing = impl_constraints_[i]; if (TypeEqual(existing.type, impl.type, std::nullopt) && TypeEqual(existing.interface, impl.interface, std::nullopt)) { - return; + return i; } } impl_constraints_.push_back(std::move(impl)); + return impl_constraints_.size() - 1; } - // Add an equality constraint -- `A == B`. + // Adds an equality constraint -- `A == B`. void AddEqualityConstraint(ConstraintType::EqualityConstraint equal) { CARBON_CHECK(equal.values.size() >= 2) << "degenerate equality constraint"; @@ -954,37 +978,96 @@ class ConstraintTypeBuilder { lookup_contexts_.push_back(std::move(context)); } - // Add all the constraints from another constraint type. The constraints must - // not refer to that other constraint type's self binding, because it will no - // longer be in scope. - void Add(Nonnull constraint) { + // Adds all the constraints from another constraint type. The given value + // `self` is substituted for `.Self`, typically specified in terms of this + // constraint's self binding. The `self_witness` is the witness for the + // resulting constraint, and can be `GetSelfWitness()`. The `bindings` + // parameter specifies any additional substitutions to perform. + void AddAndSubstitute(const TypeChecker& type_checker, + Nonnull constraint, + Nonnull self, + Nonnull self_witness, + const Bindings& bindings, bool add_lookup_contexts) { + // First substitute into the impl bindings to form the full witness for + // the constraint type. + std::vector> witnesses; for (const auto& impl_constraint : constraint->impl_constraints()) { - AddImplConstraint(impl_constraint); + Bindings local_bindings = bindings; + local_bindings.Add(constraint->self_binding(), self, + type_checker.MakeConstraintWitness( + *constraint, witnesses, + constraint->self_binding()->source_loc())); + int index = AddImplConstraint( + {.type = + type_checker.Substitute(local_bindings, impl_constraint.type), + .interface = cast(type_checker.Substitute( + local_bindings, impl_constraint.interface))}); + witnesses.push_back( + type_checker.MakeConstraintWitnessAccess(self_witness, index)); } + // Now form a complete witness and substitute it into the rest of the + // constraint. + Bindings local_bindings = bindings; + local_bindings.Add(constraint->self_binding(), self, + type_checker.MakeConstraintWitness( + *constraint, std::move(witnesses), + constraint->self_binding()->source_loc())); + for (const auto& equality_constraint : constraint->equality_constraints()) { - AddEqualityConstraint(equality_constraint); + std::vector> values; + for (const Value* value : equality_constraint.values) { + // Ensure we don't create any duplicates through substitution. + if (std::find_if(values.begin(), values.end(), [&](const Value* v) { + return ValueEqual(v, value, std::nullopt); + }) == values.end()) { + values.push_back(type_checker.Substitute(local_bindings, value)); + } + } + AddEqualityConstraint({.values = std::move(values)}); } - for (const auto& lookup_context : constraint->lookup_contexts()) { - AddLookupContext(lookup_context); + if (add_lookup_contexts) { + for (const auto& lookup_context : constraint->lookup_contexts()) { + AddLookupContext({.context = type_checker.Substitute( + local_bindings, lookup_context.context)}); + } } } - // Convert the builder into a ConstraintType. Note that this consumes the + // Brings all the `impl`s accumulated so far into the given impl scope. + void BringImplsIntoScope(const TypeChecker& type_checker, + Nonnull impl_scope) { + impl_scope->Add(impl_constraints_, llvm::None, llvm::None, GetSelfWitness(), + type_checker); + } + + // Converts the builder into a ConstraintType. Note that this consumes the // builder. auto Build(Nonnull arena_) && -> Nonnull { - return arena_->New( + // Create the new type. + auto* result = arena_->New( self_binding_, std::move(impl_constraints_), std::move(equality_constraints_), std::move(lookup_contexts_)); + // Update the impl binding to denote the constraint type itself. + impl_binding_->set_interface(result); + return result; } private: - // Make a generic binding to serve as the `.Self` of this constraint type. + // Makes a generic binding to serve as the `.Self` of this constraint type. static auto MakeSelfBinding(Nonnull arena, SourceLocation source_loc) - -> Nonnull { - Nonnull self_binding = arena->New( - source_loc, ".Self", arena->New(source_loc)); + -> Nonnull { + // Note, the type-of-type here is a placeholder and isn't really + // meaningful. + return arena->New(source_loc, ".Self", + arena->New(source_loc)); + } + + // Sets up a `.Self` binding to act as the self type of this constraint. + static auto PrepareSelfBinding(Nonnull arena, + Nonnull self_binding) + -> Nonnull { Nonnull self = arena->New(self_binding); // TODO: Do we really need both of these? self_binding->set_symbolic_identity(self); @@ -992,29 +1075,111 @@ class ConstraintTypeBuilder { return self_binding; } + // Adds an impl binding to the given self binding. + static auto AddImplBinding(Nonnull arena, + Nonnull self_binding) + -> Nonnull { + // The `.Self` binding for a constraint should always have an + // `ImplBinding`. The interface type will be set by `Build`. + Nonnull impl_binding = arena->New( + self_binding->source_loc(), self_binding, std::nullopt); + impl_binding->set_symbolic_identity( + arena->New(impl_binding)); + self_binding->set_impl_binding(impl_binding); + return impl_binding; + } + private: - Nonnull self_binding_; + Nonnull self_binding_; + Nonnull impl_binding_; std::vector impl_constraints_; std::vector equality_constraints_; std::vector lookup_contexts_; }; -auto TypeChecker::Substitute( - const std::map, Nonnull>& dict, - Nonnull type) const -> Nonnull { - auto SubstituteIntoBindings = - [&](const Bindings& bindings) -> Nonnull { - BindingMap result; - for (const auto& [name, value] : bindings.args()) { - result[name] = Substitute(dict, value); +// A collection of substituted `GenericBinding`s and `ImplBinding`s. +class TypeChecker::SubstitutedGenericBindings { + public: + SubstitutedGenericBindings(Nonnull type_checker, + Bindings bindings) + : type_checker_(type_checker), bindings_(std::move(bindings)) {} + + // Makes a new impl binding for a generic binding if needed, and returns its + // witness. + auto MakeImplBinding(Nonnull new_binding, + Nonnull old_binding) + -> std::optional> { + if (!old_binding->impl_binding()) { + return std::nullopt; } - return arena_->New(std::move(result), Bindings::NoWitnesses); + Nonnull impl_binding = + type_checker_->arena_->New(new_binding->source_loc(), + new_binding, + &new_binding->static_type()); + impl_binding->set_original(old_binding->impl_binding().value()); + auto* witness = type_checker_->arena_->New(impl_binding); + impl_binding->set_symbolic_identity(witness); + new_binding->set_impl_binding(impl_binding); + impl_bindings_.push_back(impl_binding); + return witness; + } + + // Substitutes into a generic binding and adds it to the bindings map. + auto SubstituteIntoGenericBinding(Nonnull old_binding) + -> Nonnull { + Nonnull new_type = + type_checker_->Substitute(bindings_, &old_binding->static_type()); + Nonnull new_binding = + type_checker_->arena_->New( + old_binding->source_loc(), old_binding->name(), + const_cast(&old_binding->type())); + new_binding->set_original(old_binding->original()); + new_binding->set_static_type(new_type); + bindings_.Add(old_binding, + type_checker_->arena_->New(new_binding), + MakeImplBinding(new_binding, old_binding)); + return new_binding; + } + + // Gets the current set of bindings, including any remappings for substituted + // generic bindings and impl bindings. + auto bindings() const -> const Bindings& { return bindings_; } + + // Returns ownership of the collection of created `ImplBinding`s. + auto TakeImplBindings() && -> std::vector> { + return std::move(impl_bindings_); + } + + private: + Nonnull type_checker_; + Bindings bindings_; + std::vector> impl_bindings_; +}; + +auto TypeChecker::Substitute(const Bindings& bindings, + Nonnull type) const + -> Nonnull { + auto SubstituteIntoBindings = + [&](Nonnull inner_bindings) -> Nonnull { + BindingMap values; + for (const auto& [name, value] : inner_bindings->args()) { + values[name] = Substitute(bindings, value); + } + ImplWitnessMap witnesses; + for (const auto& [name, value] : inner_bindings->witnesses()) { + witnesses[name] = Substitute(bindings, value); + } + if (values == inner_bindings->args() && + witnesses == inner_bindings->witnesses()) { + return inner_bindings; + } + return arena_->New(std::move(values), std::move(witnesses)); }; switch (type->kind()) { case Value::Kind::VariableType: { - auto it = dict.find(&cast(*type).binding()); - if (it == dict.end()) { + auto it = bindings.args().find(&cast(*type).binding()); + if (it == bindings.args().end()) { return type; } else { return it->second; @@ -1022,9 +1187,10 @@ auto TypeChecker::Substitute( } case Value::Kind::AssociatedConstant: { const auto& assoc = cast(*type); - Nonnull base = Substitute(dict, &assoc.base()); - Nonnull interface = Substitute(dict, &assoc.interface()); - Nonnull witness = Substitute(dict, &assoc.witness()); + Nonnull base = Substitute(bindings, &assoc.base()); + Nonnull interface = + Substitute(bindings, &assoc.interface()); + Nonnull witness = Substitute(bindings, &assoc.witness()); return arena_->New( base, cast(interface), &assoc.constant(), cast(witness)); @@ -1032,121 +1198,72 @@ auto TypeChecker::Substitute( case Value::Kind::TupleValue: { std::vector> elts; for (const auto& elt : cast(*type).elements()) { - elts.push_back(Substitute(dict, elt)); + elts.push_back(Substitute(bindings, elt)); } return arena_->New(elts); } case Value::Kind::StructType: { std::vector fields; for (const auto& [name, value] : cast(*type).fields()) { - auto new_type = Substitute(dict, value); + auto new_type = Substitute(bindings, value); fields.push_back({name, new_type}); } return arena_->New(std::move(fields)); } case Value::Kind::FunctionType: { const auto& fn_type = cast(*type); - std::map, Nonnull> new_dict( - dict); - // Create new generic parameters and generic bindings - // and add them to new_dict. + SubstitutedGenericBindings subst_bindings(this, bindings); + + // Apply substitution to into generic parameters and deduced bindings. std::vector generic_parameters; - std::vector> deduced_bindings; - std::map, Nonnull> - bind_map; // Map old generic bindings to new ones. for (const FunctionType::GenericParameter& gp : fn_type.generic_parameters()) { - Nonnull new_type = - Substitute(dict, &gp.binding->static_type()); - Nonnull new_gb = arena_->New( - gp.binding->source_loc(), gp.binding->name(), - (Expression*)&gp.binding->type()); // How to avoid the cast? -jsiek - new_gb->set_original(gp.binding->original()); - new_gb->set_static_type(new_type); - FunctionType::GenericParameter new_gp = {.index = gp.index, - .binding = new_gb}; - generic_parameters.push_back(new_gp); - new_dict[gp.binding] = arena_->New(new_gp.binding); - bind_map[gp.binding] = new_gb; + generic_parameters.push_back( + {.index = gp.index, + .binding = + subst_bindings.SubstituteIntoGenericBinding(gp.binding)}); } + std::vector> deduced_bindings; for (Nonnull gb : fn_type.deduced_bindings()) { - Nonnull new_type = Substitute(dict, &gb->static_type()); - Nonnull new_gb = arena_->New( - gb->source_loc(), gb->name(), - (Expression*)&gb->type()); // How to avoid the cast? -jsiek - new_gb->set_original(gb->original()); - new_gb->set_static_type(new_type); - deduced_bindings.push_back(new_gb); - new_dict[gb] = arena_->New(new_gb); - bind_map[gb] = new_gb; + deduced_bindings.push_back( + subst_bindings.SubstituteIntoGenericBinding(gb)); } - // Apply substitution to impl bindings and update their - // `type_var` pointers to the new generic bindings. - std::vector> impl_bindings; - for (auto ib : fn_type.impl_bindings()) { - Nonnull new_ib = - arena_->New(ib->source_loc(), bind_map[ib->type_var()], - Substitute(new_dict, ib->interface())); - // TODO: Should we set a symbolic identity on this impl binding? - new_ib->set_original(ib->original()); - impl_bindings.push_back(new_ib); - } - // Apply substitution to parameter types - auto param = Substitute(new_dict, &fn_type.parameters()); - // Apply substitution to return type - auto ret = Substitute(new_dict, &fn_type.return_type()); - // Create the new FunctionType - Nonnull new_fn_type = arena_->New( - param, generic_parameters, ret, deduced_bindings, impl_bindings); - return new_fn_type; + + // Apply substitution to parameter and return types and create the new + // function type. + auto param = Substitute(subst_bindings.bindings(), &fn_type.parameters()); + auto ret = Substitute(subst_bindings.bindings(), &fn_type.return_type()); + return arena_->New( + param, std::move(generic_parameters), ret, + std::move(deduced_bindings), + std::move(subst_bindings).TakeImplBindings()); } case Value::Kind::PointerType: { return arena_->New( - Substitute(dict, &cast(*type).type())); + Substitute(bindings, &cast(*type).type())); } case Value::Kind::NominalClassType: { const auto& class_type = cast(*type); Nonnull new_class_type = arena_->New( &class_type.declaration(), - SubstituteIntoBindings(class_type.bindings())); + SubstituteIntoBindings(&class_type.bindings())); return new_class_type; } case Value::Kind::InterfaceType: { const auto& iface_type = cast(*type); Nonnull new_iface_type = arena_->New( &iface_type.declaration(), - SubstituteIntoBindings(iface_type.bindings())); + SubstituteIntoBindings(&iface_type.bindings())); return new_iface_type; } case Value::Kind::ConstraintType: { const auto& constraint = cast(*type); - ConstraintTypeBuilder builder(constraint.self_binding()); - for (const auto& impl_constraint : constraint.impl_constraints()) { - builder.AddImplConstraint( - {.type = Substitute(dict, impl_constraint.type), - .interface = cast( - Substitute(dict, impl_constraint.interface))}); - } - - for (const auto& equality_constraint : - constraint.equality_constraints()) { - std::vector> values; - for (const Value* value : equality_constraint.values) { - // Ensure we don't create any duplicates through substitution. - if (std::find_if(values.begin(), values.end(), [&](const Value* v) { - return ValueEqual(v, value, std::nullopt); - }) == values.end()) { - values.push_back(Substitute(dict, value)); - } - } - builder.AddEqualityConstraint({.values = std::move(values)}); - } - - for (const auto& lookup_context : constraint.lookup_contexts()) { - builder.AddLookupContext( - {.context = Substitute(dict, lookup_context.context)}); - } + ConstraintTypeBuilder builder(arena_, + constraint.self_binding()->source_loc()); + builder.AddAndSubstitute(*this, &constraint, builder.GetSelfType(), + builder.GetSelfWitness(), bindings, + /*add_lookup_contexts=*/true); Nonnull new_constraint = std::move(builder).Build(arena_); if (trace_stream_) { @@ -1155,6 +1272,36 @@ auto TypeChecker::Substitute( } return new_constraint; } + case Value::Kind::ImplWitness: { + const auto& witness = cast(*type); + return arena_->New( + &witness.declaration(), SubstituteIntoBindings(&witness.bindings())); + } + case Value::Kind::BindingWitness: { + auto it = + bindings.witnesses().find(cast(*type).binding()); + if (it == bindings.witnesses().end()) { + return type; + } else { + return it->second; + } + } + case Value::Kind::ConstraintWitness: { + const auto& witness = cast(*type); + std::vector> witnesses; + witnesses.reserve(witness.witnesses().size()); + for (auto* witness : witness.witnesses()) { + witnesses.push_back(cast(Substitute(bindings, witness))); + } + return arena_->New(std::move(witnesses)); + } + case Value::Kind::ConstraintImplWitness: { + const auto& witness = cast(*type); + return ConstraintImplWitness::Make( + arena_, + cast(Substitute(bindings, witness.constraint_witness())), + witness.index()); + } case Value::Kind::StaticArrayType: case Value::Kind::AutoType: case Value::Kind::IntType: @@ -1175,10 +1322,6 @@ auto TypeChecker::Substitute( // TODO: We should substitute into the value and produce a new type of // type for it. return type; - case Value::Kind::ImplWitness: - case Value::Kind::BindingWitness: - case Value::Kind::ConstraintWitness: - case Value::Kind::ConstraintImplWitness: case Value::Kind::ParameterizedEntityName: case Value::Kind::MemberName: case Value::Kind::IntValue: @@ -1273,8 +1416,6 @@ auto TypeChecker::MatchImpl(const InterfaceType& iface, // Only ImplWitnesses can be parameterized. const ImplWitness* impl_witness = cast(impl.witness); - CARBON_CHECK(impl_witness->bindings().empty()) - << "should not deduce arguments for ImplWitness we have already resolved"; return arena_->New( &impl_witness->declaration(), arena_->New(std::move(deduced_args), std::move(impls))); @@ -1288,7 +1429,7 @@ auto TypeChecker::MakeConstraintWitness( } auto TypeChecker::MakeConstraintWitnessAccess(Nonnull witness, - size_t impl_offset) const + int impl_offset) const -> Nonnull { return ConstraintImplWitness::Make(arena_, witness, impl_offset); } @@ -1299,8 +1440,10 @@ auto TypeChecker::SatisfyImpls( const BindingMap& deduced_type_args, ImplWitnessMap& impls) const -> ErrorOr { for (Nonnull impl_binding : impl_bindings) { + // TODO: Presumably we will need to accumuate witnesses as we go. + Bindings bindings(deduced_type_args, Bindings::NoWitnesses); Nonnull interface = - Substitute(deduced_type_args, impl_binding->interface()); + Substitute(bindings, impl_binding->interface()); CARBON_CHECK(deduced_type_args.find(impl_binding->type_var()) != deduced_type_args.end()); CARBON_ASSIGN_OR_RETURN( @@ -1318,7 +1461,7 @@ auto TypeChecker::MakeConstraintForInterface( -> Nonnull { ConstraintTypeBuilder builder(arena_, source_loc); builder.AddImplConstraint( - {.type = builder.GetSelfType(arena_), .interface = iface_type}); + {.type = builder.GetSelfType(), .interface = iface_type}); builder.AddLookupContext({.context = iface_type}); return std::move(builder).Build(arena_); } @@ -1328,11 +1471,10 @@ auto TypeChecker::CombineConstraints( llvm::ArrayRef> constraints) -> Nonnull { ConstraintTypeBuilder builder(arena_, source_loc); - auto* self = builder.GetSelfType(arena_); for (Nonnull constraint : constraints) { - BindingMap map; - map[constraint->self_binding()] = self; - builder.Add(cast(Substitute(map, constraint))); + builder.AddAndSubstitute(*this, constraint, builder.GetSelfType(), + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true); } return std::move(builder).Build(arena_); } @@ -1405,8 +1547,7 @@ auto TypeChecker::DeduceCallBindings( // TODO: Ensure any equality constraints are satisfied. // Convert the arguments to the parameter type. - Nonnull param_type = - Substitute(call.bindings().args(), params_type); + Nonnull param_type = Substitute(call.bindings(), params_type); // Convert the arguments to the deduced and substituted parameter type. CARBON_ASSIGN_OR_RETURN( @@ -1481,6 +1622,23 @@ static auto LookupInConstraint(SourceLocation source_loc, return found.value(); } +// Determine whether the given member declaration declares an instance member. +static auto IsInstanceMember(Member member) { + if (!member.declaration()) { + // This is a struct field. + return true; + } + Nonnull declaration = *member.declaration(); + switch (declaration->kind()) { + case DeclarationKind::FunctionDeclaration: + return cast(declaration)->is_method(); + case DeclarationKind::VariableDeclaration: + return true; + default: + return false; + } +} + auto TypeChecker::TypeCheckExp(Nonnull e, const ImplScope& impl_scope) -> ErrorOr { @@ -1606,9 +1764,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, type_member.has_value()) { auto [member_type, member] = type_member.value(); Nonnull field_type = - Substitute(t_class.type_args(), member_type); + Substitute(t_class.bindings(), member_type); access.set_member(Member(member)); access.set_static_type(field_type); + access.set_is_type_access(!IsInstanceMember(access.member())); switch (member->kind()) { case DeclarationKind::VariableDeclaration: access.set_value_category(access.object().value_category()); @@ -1619,7 +1778,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, PatternKind::AddrPattern) { access.set_is_field_addr_me_method(); Nonnull me_type = - Substitute(t_class.type_args(), + Substitute(t_class.bindings(), &func_decl->me_pattern().static_type()); CARBON_RETURN_IF_ERROR(ExpectType( e->source_loc(), "method access, receiver type", me_type, @@ -1656,14 +1815,27 @@ auto TypeChecker::TypeCheckExp(Nonnull e, ConstraintLookupResult result, LookupInConstraint(e->source_loc(), "member access", &typeof_var, access.member_name())); + // Compute a witness that the variable type implements this + // interface. This will typically be either a reference to its + // `ImplBinding` or, for a constraint, to a witness for an impl + // constraint within it. + // TODO: We should only need to look at the impl binding for this + // variable, not everything in the impl scope, to find the witness. + CARBON_ASSIGN_OR_RETURN( + Nonnull witness, + impl_scope.Resolve(result.interface, &object_type, + e->source_loc(), *this)); + + Bindings bindings = result.interface->bindings(); + bindings.Add(result.interface->declaration().self(), &object_type, + witness); const Value& member_type = result.member->static_type(); - BindingMap binding_map = result.interface->args(); - binding_map[result.interface->declaration().self()] = &object_type; Nonnull inst_member_type = - Substitute(binding_map, &member_type); + Substitute(bindings, &member_type); access.set_member(Member(result.member)); access.set_found_in_interface(result.interface); + access.set_is_type_access(!IsInstanceMember(access.member())); access.set_static_type(inst_member_type); CARBON_ASSIGN_OR_RETURN( @@ -1697,22 +1869,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, access.set_impl(impl); access.set_found_in_interface(result.interface); - bool is_instance_member; - switch (result.member->kind()) { - case DeclarationKind::FunctionDeclaration: - is_instance_member = - cast(*result.member).is_method(); - break; - case DeclarationKind::AssociatedConstantDeclaration: - is_instance_member = false; - break; - default: - CARBON_FATAL() - << "unexpected kind for interface member " << *result.member; - break; - } - - if (is_instance_member) { + if (IsInstanceMember(access.member())) { // This is a member name denoting an instance member. // TODO: Consider setting the static type of all instance member // declarations to be member name types, rather than special-casing @@ -1725,10 +1882,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, // the witness table, such as a non-method function or an // associated constant. const Value& member_type = result.member->static_type(); - BindingMap binding_map = result.interface->args(); - binding_map[result.interface->declaration().self()] = type; + Bindings bindings = result.interface->bindings(); + bindings.Add(result.interface->declaration().self(), type, impl); Nonnull inst_member_type = - Substitute(binding_map, &member_type); + Substitute(bindings, &member_type); access.set_static_type(inst_member_type); access.set_value_category(ValueCategory::Let); } @@ -1773,11 +1930,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, *parameter_types; if (choice.IsParameterized()) { substituted_parameter_type = - Substitute(choice.type_args(), *parameter_types); + Substitute(choice.bindings(), *parameter_types); } Nonnull type = arena_->New( - substituted_parameter_type, llvm::None, &choice, llvm::None, - llvm::None); + substituted_parameter_type, &choice); // TODO: Should there be a Declaration corresponding to each // choice type alternative? access.set_member(Member(arena_->New( @@ -1802,7 +1958,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, break; } Nonnull field_type = Substitute( - class_type.type_args(), &member->static_type()); + class_type.bindings(), &member->static_type()); access.set_static_type(field_type); access.set_value_category(ValueCategory::Let); return Success(); @@ -1860,6 +2016,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, InterpExp(&access.path(), arena_, trace_stream_)); const auto& member_name = cast(*member_name_value); access.set_member(&member_name); + bool is_instance_member = IsInstanceMember(member_name.member()); bool has_instance = true; std::optional> base_type = member_name.base_type(); @@ -1885,6 +2042,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, &access.object(), *base_type)); access.set_object(converted_object); } + access.set_is_type_access(has_instance && !is_instance_member); // Perform impl selection if necessary. if (std::optional> iface = @@ -1899,12 +2057,13 @@ auto TypeChecker::TypeCheckExp(Nonnull e, Nonnull member_type = &member_name.member().type(); if (member_name.interface()) { Nonnull iface_type = *member_name.interface(); - BindingMap binding_map = iface_type->args(); - binding_map[iface_type->declaration().self()] = *base_type; - return Substitute(binding_map, member_type); + Bindings bindings = iface_type->bindings(); + bindings.Add(iface_type->declaration().self(), *base_type, + access.impl()); + return Substitute(bindings, member_type); } if (auto* class_type = dyn_cast(base_type.value())) { - return Substitute(class_type->type_args(), member_type); + return Substitute(class_type->bindings(), member_type); } return member_type; }; @@ -1921,11 +2080,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, } break; case DeclarationKind::FunctionDeclaration: { - bool is_method = cast(*decl.value()).is_method(); - if (has_instance || !is_method) { + if (has_instance || !is_instance_member) { // This should not be possible: the name of a static member // function should have function type not member name type. - CARBON_CHECK(!has_instance || is_method || + CARBON_CHECK(!has_instance || is_instance_member || !member_name.base_type().has_value()) << "vacuous compound member access"; access.set_static_type(SubstituteIntoMemberType()); @@ -2212,12 +2370,11 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_RETURN_IF_ERROR(DeduceCallBindings( call, &fun_t.parameters(), fun_t.generic_parameters(), fun_t.deduced_bindings(), fun_t.impl_bindings(), impl_scope)); - const BindingMap& generic_bindings = call.deduced_args(); // Substitute into the return type to determine the type of the call // expression. Nonnull return_type = - Substitute(generic_bindings, &fun_t.return_type()); + Substitute(call.bindings(), &fun_t.return_type()); call.set_static_type(return_type); call.set_value_category(ValueCategory::Let); return Success(); @@ -2248,8 +2405,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, CARBON_RETURN_IF_ERROR(DeduceCallBindings( call, ¶m_name.params().static_type(), generic_parameters, /*deduced_bindings=*/llvm::None, impl_bindings, impl_scope)); - Nonnull bindings = - arena_->New(call.deduced_args(), Bindings::NoWitnesses); + Nonnull bindings = &call.bindings(); const Declaration& decl = param_name.declaration(); switch (decl.kind()) { @@ -2511,37 +2667,43 @@ auto TypeChecker::TypeCheckExp(Nonnull e, auto& where = cast(*e); ImplScope inner_impl_scope; inner_impl_scope.AddParent(&impl_scope); - CARBON_RETURN_IF_ERROR(TypeCheckPattern(&where.self_binding(), - std::nullopt, inner_impl_scope, - ValueCategory::Let)); - for (Nonnull clause : where.clauses()) { - CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, inner_impl_scope)); - } + + // Note, we don't want to call `TypeCheckPattern` here. Most of the setup + // for the self binding is instead done by the `ConstraintTypeBuilder`. + auto& self = where.self_binding(); + CARBON_ASSIGN_OR_RETURN(Nonnull base_type, + TypeCheckTypeExp(&self.type(), impl_scope)); + self.set_static_type(base_type); std::optional> base; - const Value& base_type = where.self_binding().static_type(); - if (auto* constraint_type = dyn_cast(&base_type)) { + if (auto* constraint_type = dyn_cast(base_type)) { base = constraint_type; - } else if (auto* interface_type = dyn_cast(&base_type)) { + } else if (auto* interface_type = dyn_cast(base_type)) { base = MakeConstraintForInterface(e->source_loc(), interface_type); } else if (isa(base_type)) { // Start with an unconstrained type. } else { return ProgramError(e->source_loc()) << "expected constraint as first operand of `where` expression, " - << "found " << base_type; + << "found " << *base_type; } // Start with the given constraint, if any. - ConstraintTypeBuilder builder(&where.self_binding()); + ConstraintTypeBuilder builder(arena_, &self); if (base) { - BindingMap map; - map[(*base)->self_binding()] = builder.GetSelfType(arena_); - builder.Add(cast(Substitute(map, *base))); + builder.AddAndSubstitute(*this, *base, builder.GetSelfType(), + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/true); + // Constraints from the LHS of `where` are in scope in the RHS. But + // constraints from earlier `where` clauses are not in scope in later + // clauses. + builder.BringImplsIntoScope(*this, &inner_impl_scope); } - // Apply the `where` clauses. - for (Nonnull clause : where.clauses()) { + // Type-check and apply the `where` clauses. + for (Nonnull clause : where.clauses()) { + CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, inner_impl_scope)); + switch (clause->kind()) { case WhereClauseKind::IsWhereClause: { const auto& is_clause = cast(*clause); @@ -2559,9 +2721,9 @@ auto TypeChecker::TypeCheckExp(Nonnull e, // Transform `where .B is (C where .D is E)` into // `where .B is C and .B.D is E` then add all the resulting // constraints. - BindingMap map; - map[constraint_type->self_binding()] = type; - builder.Add(cast(Substitute(map, constraint))); + builder.AddAndSubstitute(*this, constraint_type, type, + builder.GetSelfWitness(), Bindings(), + /*add_lookup_contexts=*/false); } else { return ProgramError(is_clause.constraint().source_loc()) << "expression after `is` does not resolve to a " @@ -2786,7 +2948,6 @@ auto TypeChecker::TypeCheckPattern( "type: " << binding; } - binding.set_static_type(type); if (binding.named_as_type_via_dot_self() && !IsTypeOfType(type)) { return ProgramError(binding.type().source_loc()) << "`.Self` used in type of non-type binding `" << binding.name() @@ -2797,14 +2958,29 @@ auto TypeChecker::TypeCheckPattern( binding.set_symbolic_identity(val); SetValue(&binding, val); + // Create an impl binding if we have a constraint. if (isa(type)) { - Nonnull impl_binding = - arena_->New(binding.source_loc(), &binding, type); - impl_binding->set_symbolic_identity( - arena_->New(impl_binding)); + Nonnull impl_binding = arena_->New( + binding.source_loc(), &binding, std::nullopt); + auto* witness = arena_->New(impl_binding); + impl_binding->set_symbolic_identity(witness); binding.set_impl_binding(impl_binding); + + // Substitute the VariableType as `.Self` of the constraint to form the + // resolved type of the binding. Eg, `T:! X where .Self is Y` resolves + // to `T:! `. + if (auto* constraint = dyn_cast(type)) { + ConstraintTypeBuilder builder(arena_, binding.source_loc()); + builder.AddAndSubstitute(*this, constraint, val, witness, Bindings(), + /*add_lookup_contexts=*/true); + type = std::move(builder).Build(arena_); + } + impl_binding->set_interface(type); + BringImplIntoScope(impl_binding, impl_scope); } + + binding.set_static_type(type); return Success(); } case PatternKind::TuplePattern: { @@ -2864,7 +3040,7 @@ auto TypeChecker::TypeCheckPattern( Nonnull substituted_parameter_type = *parameter_types; if (choice_type.IsParameterized()) { substituted_parameter_type = - Substitute(choice_type.type_args(), *parameter_types); + Substitute(choice_type.bindings(), *parameter_types); } CARBON_RETURN_IF_ERROR( TypeCheckPattern(&alternative.arguments(), substituted_parameter_type, @@ -3284,8 +3460,9 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull f, CARBON_RETURN_IF_ERROR( ExpectIsConcreteType(f->source_loc(), &f->return_term().static_type())); f->set_static_type(arena_->New( - &f->param_pattern().static_type(), generic_parameters, - &f->return_term().static_type(), deduced_bindings, impl_bindings)); + &f->param_pattern().static_type(), std::move(generic_parameters), + &f->return_term().static_type(), std::move(deduced_bindings), + std::move(impl_bindings))); switch (f->kind()) { case DeclarationKind::FunctionDeclaration: f->set_constant_value( @@ -3379,15 +3556,8 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull class_decl, // For class declaration `class MyType(T:! Type, U:! AnInterface)`, `Self` // should have the value `MyType(T, U)`. - BindingMap generic_args; - for (auto* binding : bindings) { - // binding.symbolic_identity() set by call to `TypeCheckPattern(...)` - // above and/or by any enclosing generic classes. - generic_args[binding] = *binding->symbolic_identity(); - } Nonnull self_type = arena_->New( - class_decl, - arena_->New(std::move(generic_args), Bindings::NoWitnesses)); + class_decl, Bindings::SymbolicIdentity(arena_, bindings)); self->set_static_type(arena_->New(self_type)); self->set_constant_value(self_type); @@ -3604,13 +3774,8 @@ auto TypeChecker::DeclareInterfaceDeclaration( // interface itself. std::vector> bindings = scope_info.bindings; CollectGenericBindingsInPattern(*iface_decl->params(), bindings); - BindingMap generic_args; - for (auto* binding : bindings) { - generic_args[binding] = *binding->symbolic_identity(); - } iface_type = arena_->New( - iface_decl, - arena_->New(std::move(generic_args), Bindings::NoWitnesses)); + iface_decl, Bindings::SymbolicIdentity(arena_, bindings)); } else { iface_type = arena_->New(iface_decl); iface_decl->set_static_type(arena_->New(iface_type)); @@ -3695,17 +3860,15 @@ auto TypeChecker::CheckImplIsDeducible( auto TypeChecker::CheckImplIsComplete(Nonnull iface_type, Nonnull impl_decl, Nonnull self_type, + Nonnull self_witness, const ImplScope& impl_scope) -> ErrorOr { const auto& iface_decl = iface_type->declaration(); for (Nonnull m : iface_decl.members()) { if (auto* assoc = dyn_cast(m)) { // An associated constant must be given exactly one value. - Nonnull symbolic_self = - impl_decl->constraint_type()->self_binding(); Nonnull expected = arena_->New( - &symbolic_self->value(), iface_type, assoc, - arena_->New(impl_decl)); + self_type, iface_type, assoc, self_witness); bool found_any = false; std::optional> found_value; @@ -3726,7 +3889,8 @@ auto TypeChecker::CheckImplIsComplete(Nonnull iface_type, impl_decl->constraint_type()->VisitEqualValues(expected, visitor); if (!found_any) { return ProgramError(impl_decl->source_loc()) - << "implementation missing " << *expected; + << "implementation missing " << *expected << "; have " + << *impl_decl->constraint_type(); } else if (!found_value) { // TODO: It's not clear what the right rule is here. Clearly // impl T as HasX & HasY where .X == .Y {} @@ -3753,10 +3917,10 @@ auto TypeChecker::CheckImplIsComplete(Nonnull iface_type, << "implementation missing " << *mem_name; } - BindingMap binding_map = iface_type->args(); - binding_map[iface_decl.self()] = self_type; + Bindings bindings = iface_type->bindings(); + bindings.Add(iface_decl.self(), self_type, self_witness); Nonnull iface_mem_type = - Substitute(binding_map, &m->static_type()); + Substitute(bindings, &m->static_type()); // TODO: How should the signature in the implementation be permitted // to differ from the signature in the interface? CARBON_RETURN_IF_ERROR( @@ -3769,7 +3933,8 @@ auto TypeChecker::CheckImplIsComplete(Nonnull iface_type, auto TypeChecker::CheckAndAddImplBindings( Nonnull impl_decl, Nonnull impl_type, - const ScopeInfo& scope_info) -> ErrorOr { + Nonnull self_witness, const ScopeInfo& scope_info) + -> ErrorOr { // The deduced bindings are the parameters for all enclosing classes followed // by any deduced parameters written on the `impl` declaration itself. std::vector> deduced_bindings = @@ -3778,19 +3943,10 @@ auto TypeChecker::CheckAndAddImplBindings( impl_decl->deduced_parameters().begin(), impl_decl->deduced_parameters().end()); - // An expression that evaluates to this impl's witness. - auto* witness = arena_->New(impl_decl); - - // Form the resolved constraint type by substituting `Self` for `.Self`. - Nonnull self = *impl_decl->self()->constant_value(); - BindingMap constraint_self_map; - constraint_self_map[impl_decl->constraint_type()->self_binding()] = self; - Nonnull constraint = cast( - Substitute(constraint_self_map, impl_decl->constraint_type())); - // Each interface that is a lookup context is required to be implemented by // the impl members. Other constraints are required to be satisfied by // either those impls or impls available elsewhere. + Nonnull constraint = impl_decl->constraint_type(); for (auto lookup : constraint->lookup_contexts()) { if (auto* iface_type = dyn_cast(lookup.context)) { CARBON_RETURN_IF_ERROR( @@ -3802,15 +3958,18 @@ auto TypeChecker::CheckAndAddImplBindings( // these values. ImplScope iface_scope; iface_scope.AddParent(scope_info.innermost_scope); - BringAssociatedConstantsIntoScope(cast(constraint), self, - iface_type, iface_scope); + BringAssociatedConstantsIntoScope(constraint, impl_type, iface_type, + iface_scope); - CARBON_RETURN_IF_ERROR( - CheckImplIsComplete(iface_type, impl_decl, impl_type, iface_scope)); + CARBON_RETURN_IF_ERROR(CheckImplIsComplete( + iface_type, impl_decl, impl_type, self_witness, iface_scope)); + // TODO: We should do this either before checking any interface or after + // checking all of them, so that the order of lookup contexts doesn't + // matter. scope_info.innermost_non_class_scope->Add( iface_type, deduced_bindings, impl_type, impl_decl->impl_bindings(), - witness, *this); + self_witness, *this); } else { // TODO: Add support for implementing `adapter`s. return ProgramError(impl_decl->source_loc()) @@ -3864,7 +4023,24 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull impl_decl, << "expected constraint after `as`, found value of type " << *constraint_type; } - impl_decl->set_constraint_type(cast(constraint_type)); + + // Build the self-witness. This is the witness used to demonstrate that this + // impl implements its constraint. + auto* self_witness = arena_->New( + impl_decl, + Bindings::SymbolicIdentity(arena_, impl_decl->deduced_parameters())); + + // Substitute the given type for `.Self` to form the resolved constraint that + // this `impl` implements. + { + ConstraintTypeBuilder builder(arena_, impl_decl->source_loc()); + builder.AddAndSubstitute(*this, cast(constraint_type), + impl_type_value, self_witness, Bindings(), + /*add_lookup_contexts=*/true); + auto* resolved = std::move(builder).Build(arena_); + impl_decl->set_constraint_type(cast(resolved)); + constraint_type = resolved; + } // Declare the impl members. ScopeInfo impl_scope_info = ScopeInfo::ForNonClassScope(&impl_scope); @@ -3873,8 +4049,8 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull impl_decl, } // Create the implied impl bindings. - CARBON_RETURN_IF_ERROR( - CheckAndAddImplBindings(impl_decl, impl_type_value, scope_info)); + CARBON_RETURN_IF_ERROR(CheckAndAddImplBindings(impl_decl, impl_type_value, + self_witness, scope_info)); // Check the constraint is satisfied by the `impl`s we just created. This // serves a couple of purposes: @@ -3923,12 +4099,8 @@ auto TypeChecker::TypeCheckImplDeclaration(Nonnull impl_decl, **trace_stream_ << "checking " << *impl_decl << "\n"; } - // Form the resolved constraint type by substituting `Self` for `.Self`. Nonnull self = *impl_decl->self()->constant_value(); - BindingMap constraint_self_map; - constraint_self_map[impl_decl->constraint_type()->self_binding()] = self; - Nonnull constraint = cast( - Substitute(constraint_self_map, impl_decl->constraint_type())); + Nonnull constraint = impl_decl->constraint_type(); // Bring the impls from the parameters into scope. ImplScope impl_scope; @@ -3970,10 +4142,6 @@ auto TypeChecker::DeclareChoiceDeclaration(Nonnull choice, **trace_stream_ << choice_scope; } } - BindingMap generic_args; - for (auto* binding : bindings) { - generic_args[binding] = *binding->symbolic_identity(); - } std::vector alternatives; for (Nonnull alternative : choice->alternatives()) { @@ -3993,8 +4161,7 @@ auto TypeChecker::DeclareChoiceDeclaration(Nonnull choice, } auto ct = arena_->New( - choice, - arena_->New(std::move(generic_args), Bindings::NoWitnesses)); + choice, Bindings::SymbolicIdentity(arena_, bindings)); choice->set_static_type(arena_->New(ct)); choice->set_constant_value(ct); @@ -4284,8 +4451,10 @@ auto TypeChecker::FindMixedMemberAndType( FindMixedMemberAndType(name, mixin->declaration().members(), mixin); if (res.has_value()) { if (isa(enclosing_type)) { - BindingMap temp_map; - temp_map[mixin->declaration().self()] = enclosing_type; + Bindings temp_map; + // TODO: What is the type of Self? Do we ever need a witness? + temp_map.Add(mixin->declaration().self(), enclosing_type, + std::nullopt); const auto mix_member_type = Substitute(temp_map, res.value().first); return std::make_pair(mix_member_type, res.value().second); } else { diff --git a/explorer/interpreter/type_checker.h b/explorer/interpreter/type_checker.h index a54c3431332c..6b8e7868ec0b 100644 --- a/explorer/interpreter/type_checker.h +++ b/explorer/interpreter/type_checker.h @@ -56,11 +56,10 @@ class TypeChecker { const ImplScope& impl_scope) const -> ErrorOr; // Construct a type that is the same as `type` except that occurrences - // of type variables (aka. `GenericBinding`) are replaced by their - // corresponding type in `dict`. - auto Substitute(const std::map, - Nonnull>& dict, - Nonnull type) const -> Nonnull; + // of type variables (aka. `GenericBinding` and references to `ImplBinding`) + // are replaced by their corresponding type or witness in `dict`. + auto Substitute(const Bindings& dict, Nonnull type) const + -> Nonnull; // If `impl` can be an implementation of interface `iface` for the given // `type`, then return the witness for this `impl`. Otherwise return @@ -94,11 +93,12 @@ class TypeChecker { // Given the witnesses for the components of a constraint, form a witness for // the constraint. auto MakeConstraintWitnessAccess(Nonnull witness, - size_t impl_offset) const + int impl_offset) const -> Nonnull; private: struct SingleStepEqualityContext; + class SubstitutedGenericBindings; // Information about the currently enclosing scopes. struct ScopeInfo { @@ -220,12 +220,14 @@ class TypeChecker { auto CheckImplIsComplete(Nonnull iface_type, Nonnull impl_decl, Nonnull self_type, + Nonnull self_witness, const ImplScope& impl_scope) -> ErrorOr; // Check that an `impl` declaration satisfies its constraints and add the // corresponding `ImplBinding`s to the impl scope. auto CheckAndAddImplBindings(Nonnull impl_decl, Nonnull impl_type, + Nonnull self_witness, const ScopeInfo& scope_info) -> ErrorOr; auto DeclareImplDeclaration(Nonnull impl_decl, diff --git a/explorer/interpreter/value.cpp b/explorer/interpreter/value.cpp index 280de758c71e..b5e4109e2ea7 100644 --- a/explorer/interpreter/value.cpp +++ b/explorer/interpreter/value.cpp @@ -98,7 +98,7 @@ static auto GetMember(Nonnull arena, Nonnull v, const auto& class_type = cast(object.type()); std::optional> func = class_type.FindFunction(f); - if (func == std::nullopt) { + if (!func) { return ProgramError(source_loc) << "member " << f << " not in " << *v << " or its " << class_type; } else if ((*func)->declaration().is_method()) { @@ -108,6 +108,7 @@ static auto GetMember(Nonnull arena, Nonnull v, &class_type.bindings()); } else { // Found a class function + // TODO: This should not be reachable. return arena->New(&(*func)->declaration(), &class_type.bindings()); } @@ -499,7 +500,7 @@ void Value::Print(llvm::raw_ostream& out) const { out << "choice " << cast(*this).name(); break; case Value::Kind::VariableType: - out << cast(*this).binding(); + out << cast(*this).binding().name(); break; case Value::Kind::AssociatedConstant: { const auto& assoc = cast(*this); @@ -1065,11 +1066,11 @@ auto FindMember(std::string_view name, } void ImplBinding::Print(llvm::raw_ostream& out) const { - out << "impl binding " << *type_var_ << " as " << *iface_; + out << "impl binding " << *type_var_ << " as " << **iface_; } void ImplBinding::PrintID(llvm::raw_ostream& out) const { - out << *type_var_ << " as " << *iface_; + out << *type_var_ << " as " << **iface_; } } // namespace Carbon diff --git a/explorer/interpreter/value.h b/explorer/interpreter/value.h index bb8800da8cab..037826c2d1aa 100644 --- a/explorer/interpreter/value.h +++ b/explorer/interpreter/value.h @@ -510,16 +510,20 @@ class FunctionType : public Value { }; FunctionType(Nonnull parameters, - llvm::ArrayRef generic_parameters, + Nonnull return_type) + : FunctionType(parameters, {}, return_type, {}, {}) {} + + FunctionType(Nonnull parameters, + std::vector generic_parameters, Nonnull return_type, - llvm::ArrayRef> deduced_bindings, - llvm::ArrayRef> impl_bindings) + std::vector> deduced_bindings, + std::vector> impl_bindings) : Value(Kind::FunctionType), parameters_(parameters), - generic_parameters_(generic_parameters), + generic_parameters_(std::move(generic_parameters)), return_type_(return_type), - deduced_bindings_(deduced_bindings), - impl_bindings_(impl_bindings) {} + deduced_bindings_(std::move(deduced_bindings)), + impl_bindings_(std::move(impl_bindings)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::FunctionType; @@ -842,13 +846,7 @@ class Witness : public Value { // The witness table for an impl. class ImplWitness : public Witness { public: - // Construct a witness for - // 1) a non-generic impl, or - // 2) a generic impl that has not yet been applied to type arguments. - explicit ImplWitness(Nonnull declaration) - : Witness(Kind::ImplWitness), declaration_(declaration) {} - - // Construct an instantiated generic impl. + // Construct a witness for an impl. explicit ImplWitness(Nonnull declaration, Nonnull bindings) : Witness(Kind::ImplWitness), diff --git a/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon b/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon index 01a6c23fb856..aef1e3e6479e 100644 --- a/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon +++ b/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon @@ -14,7 +14,7 @@ interface HasThreeTypes { let C:! Type; } -// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon:[[@LINE+1]]: implementation missing (.Self:! HasThreeTypes).B +// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon:[[@LINE+1]]: implementation missing (i32).B; have constraint interface HasThreeTypes where i32 is interface HasThreeTypes and (i32).A == i32 and (i32).C == i32 external impl i32 as HasThreeTypes where .A == i32 and .C == i32 {} fn Main() -> i32 { return 0; } diff --git a/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon b/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon index 82af6707ffeb..6f925bea5008 100644 --- a/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon +++ b/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon @@ -14,7 +14,7 @@ interface HasThreeTypes { let C:! Type; } -// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon:[[@LINE+1]]: implementation doesn't provide a concrete value for (.Self:! HasThreeTypes).B +// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon:[[@LINE+1]]: implementation doesn't provide a concrete value for (i32).B external impl i32 as HasThreeTypes where .A == i32 and .B == .C {} fn Main() -> i32 { return 0; } diff --git a/explorer/testdata/assoc_const/fail_indirectly_equal.carbon b/explorer/testdata/assoc_const/fail_indirectly_equal.carbon index 4afbd6845c1a..e19c5cecef22 100644 --- a/explorer/testdata/assoc_const/fail_indirectly_equal.carbon +++ b/explorer/testdata/assoc_const/fail_indirectly_equal.carbon @@ -23,7 +23,7 @@ fn F2[U:! A where .T == i32](x: i32) -> U.T { } fn F3[T:! A where .T == i32, U:! A where .T == i32](x: T.T) -> U.T { - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_indirectly_equal.carbon:[[@LINE+1]]: type error in return value: '(T:! A where .Self.T == i32).T' is not implicitly convertible to '(U:! A where .Self.T == i32).T' + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_indirectly_equal.carbon:[[@LINE+1]]: type error in return value: '(T).T' is not implicitly convertible to '(U).T' return x; } diff --git a/explorer/testdata/assoc_const/fail_match_in_deduction.carbon b/explorer/testdata/assoc_const/fail_match_in_deduction.carbon index f16a0f378408..2616d52b9502 100644 --- a/explorer/testdata/assoc_const/fail_match_in_deduction.carbon +++ b/explorer/testdata/assoc_const/fail_match_in_deduction.carbon @@ -22,7 +22,7 @@ fn F[Scalar:! Type, V:! Vector where .Dim == 3](p: Point(Scalar, V.Dim), v: V) { fn G[Scalar:! Type](p: Point(Scalar, 3)) {} fn H[V:! Vector where .Dim == 3](v: V) { var p: Point(i32, V.Dim) = {}; - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_match_in_deduction.carbon:[[@LINE+1]]: mismatch in non-type values, `(V:! Vector where .Self.Dim == 3).Dim` != `3` + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_match_in_deduction.carbon:[[@LINE+1]]: mismatch in non-type values, `(V).Dim` != `3` G(p); } diff --git a/explorer/testdata/assoc_const/fail_multiple_deduction.carbon b/explorer/testdata/assoc_const/fail_multiple_deduction.carbon index c9a2b75846fa..30cc7875cb04 100644 --- a/explorer/testdata/assoc_const/fail_multiple_deduction.carbon +++ b/explorer/testdata/assoc_const/fail_multiple_deduction.carbon @@ -17,8 +17,8 @@ interface HasThreeTypes { fn F[T:! Type](x: (T, T, T)); fn G[X:! HasThreeTypes where .A == .B and .B == .C and .C == .A](x: X) { // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_multiple_deduction.carbon:[[@LINE+3]]: deduced multiple different values for T:! Type: - // CHECK: (X:! HasThreeTypes where .Self.A == .Self.B and .Self.B == .Self.C and .Self.C == .Self.A).A - // CHECK: (X:! HasThreeTypes where .Self.A == .Self.B and .Self.B == .Self.C and .Self.C == .Self.A).B + // CHECK: (X).A + // CHECK: (X).B F(x.Make()); } diff --git a/explorer/testdata/assoc_const/fail_overspecified_impl.carbon b/explorer/testdata/assoc_const/fail_overspecified_impl.carbon index 01bce3dfcad0..a3738d741459 100644 --- a/explorer/testdata/assoc_const/fail_overspecified_impl.carbon +++ b/explorer/testdata/assoc_const/fail_overspecified_impl.carbon @@ -12,7 +12,7 @@ interface HasType { let T:! Type; } -// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_overspecified_impl.carbon:[[@LINE+1]]: implementation provides multiple values for (.Self:! HasType).T: i32 and {.a: i32} +// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_overspecified_impl.carbon:[[@LINE+1]]: implementation provides multiple values for (i32).T: i32 and {.a: i32} external impl i32 as HasType where .T == i32 and .T == {.a: i32} {} fn Main() -> i32 { return 0; } diff --git a/explorer/testdata/assoc_const/fail_unknown_value.carbon b/explorer/testdata/assoc_const/fail_unknown_value.carbon index 1ceecaedab9b..5e3ca25591dd 100644 --- a/explorer/testdata/assoc_const/fail_unknown_value.carbon +++ b/explorer/testdata/assoc_const/fail_unknown_value.carbon @@ -13,7 +13,7 @@ interface Iface { let N:! i32; } fn PickType(N: i32) -> Type { return i32; } fn F[T:! Iface](x: T) -> i32 { - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value.carbon:[[@LINE+1]]: value of associated constant (T:! Iface).N is not known + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value.carbon:[[@LINE+1]]: value of associated constant (T).N is not known var x: PickType(T.N) = 0; return x; } diff --git a/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon b/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon index 9ad53998bf78..71262e2cd51d 100644 --- a/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon +++ b/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon @@ -14,7 +14,7 @@ fn PickType(N: i32) -> Type { return i32; } fn F[T:! Iface where .N == 5](x: T) -> i32 { // TODO: This should be valid: the value of T.N is known to be 5 here. - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon:[[@LINE+1]]: value of associated constant (T:! Iface where .Self.N == 5).N is not known + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon:[[@LINE+1]]: value of associated constant (T).N is not known var x: PickType(T.N) = 0; return x; } diff --git a/explorer/testdata/assoc_const/implement.carbon b/explorer/testdata/assoc_const/implement.carbon index 2daf740a1ec4..a598888538fa 100644 --- a/explorer/testdata/assoc_const/implement.carbon +++ b/explorer/testdata/assoc_const/implement.carbon @@ -20,6 +20,5 @@ class Point { } fn Main() -> i32 { - var a: Point = {.x = 2, .y = 1}; - return a.(Vector.Dim); + return Point.(Vector.Dim); } diff --git a/explorer/testdata/assoc_const/member_of_value.carbon b/explorer/testdata/assoc_const/member_of_value.carbon new file mode 100644 index 000000000000..2daf740a1ec4 --- /dev/null +++ b/explorer/testdata/assoc_const/member_of_value.carbon @@ -0,0 +1,25 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// RUN: %{explorer} %s 2>&1 | %{FileCheck-strict} %s +// RUN: %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s +// AUTOUPDATE: %{explorer} %s +// CHECK:result: 2 + +package ExplorerTest api; + +interface Vector { + let Dim:! i32; +} + +class Point { + var x: i32; + var y: i32; + impl as Vector where .Dim == 2 {} +} + +fn Main() -> i32 { + var a: Point = {.x = 2, .y = 1}; + return a.(Vector.Dim); +} diff --git a/explorer/testdata/constraint/missing_member.carbon b/explorer/testdata/constraint/missing_member.carbon index 96ca63b9d918..1cae2211f55f 100644 --- a/explorer/testdata/constraint/missing_member.carbon +++ b/explorer/testdata/constraint/missing_member.carbon @@ -11,7 +11,7 @@ package ExplorerTest api; interface A { fn F() -> i32; } interface B { fn G() -> i32; } -// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/missing_member.carbon:[[@LINE+1]]: member access, H not in constraint interface A & interface B where .Self:! Type is interface A and .Self:! Type is interface B +// CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/missing_member.carbon:[[@LINE+1]]: member access, H not in constraint interface A & interface B where T is interface A and T is interface B fn Get[T:! A & B](n: T) -> i32 { return n.H(); } impl i32 as A { diff --git a/explorer/testdata/constraint/no_combine_equality.carbon b/explorer/testdata/constraint/no_combine_equality.carbon index 023d4f46e280..19ab601ec7b4 100644 --- a/explorer/testdata/constraint/no_combine_equality.carbon +++ b/explorer/testdata/constraint/no_combine_equality.carbon @@ -13,7 +13,7 @@ impl i32 as I {} fn F(A:! i32, B:! i32, C:! i32, D:! i32, E:! i32, T:! I where A == B and C == D and C == E and B == D) { - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/no_combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where .Self:! I is interface I and A:! i32 == B:! i32 and C:! i32 == D:! i32 and C:! i32 == E:! i32 and B:! i32 == D:! i32 + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/no_combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where T is interface I and A == B and C == D and C == E and B == D T.F(); } diff --git a/explorer/testdata/generic_class/fail_self_with_arg.carbon b/explorer/testdata/generic_class/fail_self_with_arg.carbon index 82fd856e121b..34e1b1907ac7 100644 --- a/explorer/testdata/generic_class/fail_self_with_arg.carbon +++ b/explorer/testdata/generic_class/fail_self_with_arg.carbon @@ -11,7 +11,7 @@ package ExplorerTest api; class Point(T:! Type) { // Error: wrote `Self(T)` instead of `Self`. - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_self_with_arg.carbon:[[@LINE+1]]: in call `Self(T)`, expected callee to be a function, found `typeof(class Point(T = T:! Type))` + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_self_with_arg.carbon:[[@LINE+1]]: in call `Self(T)`, expected callee to be a function, found `typeof(class Point(T = T))` fn Origin(zero: T) -> Self(T) { return {.x = zero, .y = zero}; } diff --git a/explorer/testdata/generic_class/fail_two_arg_lists.carbon b/explorer/testdata/generic_class/fail_two_arg_lists.carbon index 1cb9b2c69a79..ed7a02599713 100644 --- a/explorer/testdata/generic_class/fail_two_arg_lists.carbon +++ b/explorer/testdata/generic_class/fail_two_arg_lists.carbon @@ -9,7 +9,7 @@ package ExplorerTest api; class Point(T:! Type) { - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_two_arg_lists.carbon:[[@LINE+1]]: in call `Point(T)(T)`, expected callee to be a function, found `typeof(class Point(T = T:! Type))` + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_two_arg_lists.carbon:[[@LINE+1]]: in call `Point(T)(T)`, expected callee to be a function, found `typeof(class Point(T = T))` fn Origin(zero: T) -> Point(T)(T) { return {.x = zero, .y = zero}; } diff --git a/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon b/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon index f59a4ca4de65..255fe071fa3d 100644 --- a/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon +++ b/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon @@ -11,7 +11,7 @@ package ExplorerTest api; fn Bad[T:! Type](x: {.a: i32, .b: T}) {} fn Main() -> i32 { - // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon:[[@LINE+1]]: mismatch in field names, source field `c` not in destination type `{.a: i32, .b: T:! Type}` + // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_implicit_conversion_extra_field.carbon:[[@LINE+1]]: mismatch in field names, source field `c` not in destination type `{.a: i32, .b: T}` Bad({.b = 5, .a = 7, .c = 2}); return 0; } diff --git a/explorer/testdata/generic_function/fail_not_addable.carbon b/explorer/testdata/generic_function/fail_not_addable.carbon index 492205f3513a..d991f8560ea5 100644 --- a/explorer/testdata/generic_function/fail_not_addable.carbon +++ b/explorer/testdata/generic_function/fail_not_addable.carbon @@ -10,7 +10,7 @@ package ExplorerTest api; fn id[T:! Type](x: T) -> T { // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/generic_function/fail_not_addable.carbon:[[@LINE+2]]: type error in `+`: - // CHECK:could not find implementation of interface AddWith(U = i32) for T:! Type + // CHECK:could not find implementation of interface AddWith(U = i32) for T return x + 0; }