mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
Check equality constraints when checking whether a constraint is satisfied (#2294)
Add checking that a type only satisfies a constraint if it satisfies all of that constraint's equality and rewrite constraints. Enforce the rule that `=` must be used in impls when specifying associated constant values rather than `==`. Turn off the pre-#2173 single-step equality behavior. This is getting somewhat ahead of the approved design, but it's a one-line change to restore the old behavior. Fix `CARBON_CHECK` to handle top-level `,`s in its argument, such as may happen in template argument lists, as this change introduces such a check.
This commit is contained in:
+8
-8
@@ -21,18 +21,18 @@ namespace Carbon {
|
||||
//
|
||||
// For example:
|
||||
// CARBON_CHECK(is_valid) << "Data is not valid!";
|
||||
#define CARBON_CHECK(condition) \
|
||||
(condition) ? (void)0 \
|
||||
: CARBON_RAW_EXITING_STREAM() \
|
||||
<< "CHECK failure at " << __FILE__ << ":" << __LINE__ \
|
||||
<< ": " #condition \
|
||||
<< Carbon::Internal::ExitingStream::AddSeparator()
|
||||
#define CARBON_CHECK(...) \
|
||||
(__VA_ARGS__) ? (void)0 \
|
||||
: CARBON_RAW_EXITING_STREAM() \
|
||||
<< "CHECK failure at " << __FILE__ << ":" << __LINE__ \
|
||||
<< ": " #__VA_ARGS__ \
|
||||
<< Carbon::Internal::ExitingStream::AddSeparator()
|
||||
|
||||
// DCHECK calls CHECK in debug mode, and does nothing otherwise.
|
||||
#ifndef NDEBUG
|
||||
#define CARBON_DCHECK(condition) CARBON_CHECK(condition)
|
||||
#define CARBON_DCHECK(...) CARBON_CHECK(__VA_ARGS__)
|
||||
#else
|
||||
#define CARBON_DCHECK(condition) CARBON_CHECK(true || (condition))
|
||||
#define CARBON_DCHECK(...) CARBON_CHECK(true || (__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
// This is similar to CHECK, but is unconditional. Writing CARBON_FATAL() is
|
||||
|
||||
@@ -18,9 +18,21 @@ interface ImplicitAs(T:! Type) {
|
||||
extends As(T);
|
||||
}
|
||||
|
||||
// Every type implicitly converts to itself.
|
||||
impl forall [T:! Type] T as ImplicitAs(T) {
|
||||
fn Convert[me: Self]() -> T { return me; }
|
||||
// TODO: Should we just use an intrinsic for this?
|
||||
interface __EqualConverter {
|
||||
let T:! Type;
|
||||
fn Convert(t: T) -> Self;
|
||||
}
|
||||
fn __EqualConvert[T:! Type](t: T, U:! __EqualConverter where .T = T) -> U {
|
||||
return U.Convert(t);
|
||||
}
|
||||
impl forall [U:! Type] U as __EqualConverter where .T = U {
|
||||
fn Convert(u: U) -> U { return u; }
|
||||
}
|
||||
|
||||
// Every type implicitly converts to single-step-equal types.
|
||||
impl forall [T:! Type, U:! Type where .Self == T] T as ImplicitAs(U) {
|
||||
fn Convert[me: Self]() -> U { return __EqualConvert(me, U); }
|
||||
}
|
||||
|
||||
// TODO: Simplify this once we have variadics.
|
||||
|
||||
@@ -103,7 +103,34 @@ auto ImplScope::Resolve(Nonnull<const Value*> constraint_type,
|
||||
source_loc, type_checker));
|
||||
witnesses.push_back(result);
|
||||
}
|
||||
// TODO: Check satisfaction of same-type constraints.
|
||||
|
||||
// Check that all equality constraints are satisfied in this scope.
|
||||
if (llvm::ArrayRef<EqualityConstraint> equals =
|
||||
constraint->equality_constraints();
|
||||
!equals.empty()) {
|
||||
std::optional<Nonnull<const Witness*>> witness;
|
||||
if (constraint->self_binding()->impl_binding()) {
|
||||
witness = type_checker.MakeConstraintWitness(*constraint, witnesses,
|
||||
source_loc);
|
||||
}
|
||||
Bindings local_bindings = bindings;
|
||||
local_bindings.Add(constraint->self_binding(), impl_type, witness);
|
||||
SingleStepEqualityContext equality_ctx(this);
|
||||
for (auto& equal : equals) {
|
||||
auto it = equal.values.begin();
|
||||
Nonnull<const Value*> first =
|
||||
type_checker.Substitute(local_bindings, *it++);
|
||||
for (; it != equal.values.end(); ++it) {
|
||||
Nonnull<const Value*> current =
|
||||
type_checker.Substitute(local_bindings, *it);
|
||||
if (!ValueEqual(first, current, &equality_ctx)) {
|
||||
return ProgramError(source_loc)
|
||||
<< "constraint requires that " << *first
|
||||
<< " == " << *current << ", which is not known to be true";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return type_checker.MakeConstraintWitness(*constraint, std::move(witnesses),
|
||||
source_loc);
|
||||
}
|
||||
@@ -233,4 +260,10 @@ void ImplScope::Print(llvm::raw_ostream& out) const {
|
||||
}
|
||||
}
|
||||
|
||||
auto SingleStepEqualityContext::VisitEqualValues(
|
||||
Nonnull<const Value*> value,
|
||||
llvm::function_ref<bool(Nonnull<const Value*>)> visitor) const -> bool {
|
||||
return impl_scope_->VisitEqualValues(value, visitor);
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -153,6 +153,26 @@ class ImplScope {
|
||||
std::vector<Nonnull<const ImplScope*>> parent_scopes_;
|
||||
};
|
||||
|
||||
// An equality context that considers two values to be equal if they are a
|
||||
// single step apart according to an equality constraint in the given impl
|
||||
// scope.
|
||||
struct SingleStepEqualityContext : public EqualityContext {
|
||||
public:
|
||||
SingleStepEqualityContext(Nonnull<const ImplScope*> impl_scope)
|
||||
: impl_scope_(impl_scope) {}
|
||||
|
||||
// Visits the values that are equal to the given value and a single step away
|
||||
// according to an equality constraint that is in the given impl scope. Stops
|
||||
// and returns `false` if the visitor returns `false`, otherwise returns
|
||||
// `true`.
|
||||
auto VisitEqualValues(Nonnull<const Value*> value,
|
||||
llvm::function_ref<bool(Nonnull<const Value*>)> visitor)
|
||||
const -> bool override;
|
||||
|
||||
private:
|
||||
Nonnull<const ImplScope*> impl_scope_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_EXPLORER_INTERPRETER_IMPL_SCOPE_H_
|
||||
|
||||
@@ -538,35 +538,47 @@ auto Interpreter::EvalRecursively(std::unique_ptr<Action> action)
|
||||
auto Interpreter::EvalAssociatedConstant(
|
||||
Nonnull<const AssociatedConstant*> assoc, SourceLocation source_loc)
|
||||
-> ErrorOr<Nonnull<const Value*>> {
|
||||
// Find the witness.
|
||||
// Instantiate the associated constant.
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base,
|
||||
InstantiateType(&assoc->base(), source_loc));
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> interface,
|
||||
InstantiateType(&assoc->interface(), source_loc));
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Witness*> witness,
|
||||
InstantiateWitness(&assoc->witness()));
|
||||
if (!isa<ImplWitness>(witness)) {
|
||||
Nonnull<const AssociatedConstant*> instantiated_assoc =
|
||||
arena_->New<AssociatedConstant>(base, cast<InterfaceType>(interface),
|
||||
&assoc->constant(), witness);
|
||||
|
||||
auto* impl_witness = dyn_cast<ImplWitness>(witness);
|
||||
if (!impl_witness) {
|
||||
CARBON_CHECK(phase() == Phase::CompileTime)
|
||||
<< "symbolic witnesses should only be formed at compile time";
|
||||
return ProgramError(source_loc)
|
||||
<< "value of associated constant " << *assoc << " is not known";
|
||||
return instantiated_assoc;
|
||||
}
|
||||
|
||||
auto& impl_witness = cast<ImplWitness>(*witness);
|
||||
// We have an impl. Extract the value from it.
|
||||
Nonnull<const ConstraintType*> constraint =
|
||||
impl_witness.declaration().constraint_type();
|
||||
impl_witness->declaration().constraint_type();
|
||||
std::optional<Nonnull<const Value*>> result;
|
||||
constraint->VisitEqualValues(assoc, [&](Nonnull<const Value*> 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<AssociatedConstant>(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;
|
||||
});
|
||||
// TODO: We should pick the value from the rewrite constraint, not some other
|
||||
// equality constraint that happens to be in the impl's constraint type.
|
||||
constraint->VisitEqualValues(instantiated_assoc,
|
||||
[&](Nonnull<const Value*> 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<AssociatedConstant>(equal_value)) {
|
||||
return true;
|
||||
}
|
||||
result = equal_value;
|
||||
return false;
|
||||
});
|
||||
if (!result) {
|
||||
CARBON_FATAL() << impl_witness.declaration() << " with constraint "
|
||||
CARBON_FATAL() << impl_witness->declaration() << " with constraint "
|
||||
<< *constraint
|
||||
<< " is missing value for associated constant " << *assoc;
|
||||
<< " is missing value for associated constant "
|
||||
<< *instantiated_assoc;
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
@@ -585,6 +597,14 @@ auto Interpreter::InstantiateType(Nonnull<const Value*> type,
|
||||
}
|
||||
return value;
|
||||
}
|
||||
case Value::Kind::InterfaceType: {
|
||||
const auto& interface_type = cast<InterfaceType>(*type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Bindings*> bindings,
|
||||
InstantiateBindings(&interface_type.bindings(), source_loc));
|
||||
return arena_->New<InterfaceType>(&interface_type.declaration(),
|
||||
bindings);
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& class_type = cast<NominalClassType>(*type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
@@ -603,7 +623,7 @@ auto Interpreter::InstantiateType(Nonnull<const Value*> type,
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> type_value,
|
||||
EvalAssociatedConstant(cast<AssociatedConstant>(type), source_loc));
|
||||
return InstantiateType(type_value, source_loc);
|
||||
return type_value;
|
||||
}
|
||||
default:
|
||||
return type;
|
||||
@@ -715,9 +735,12 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
InstantiateType(destination_type, source_loc));
|
||||
return arena_->New<NominalClassValue>(inst_dest, value);
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL() << "Can't convert value " << *value << " to type "
|
||||
<< *destination_type;
|
||||
default: {
|
||||
CARBON_CHECK(IsValueKindDependent(destination_type))
|
||||
<< "Can't convert value " << *value << " to type "
|
||||
<< *destination_type;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
case Value::Kind::StructType: {
|
||||
@@ -747,9 +770,12 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
&array_type.element_type());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL() << "Can't convert value " << *value << " to type "
|
||||
<< *destination_type;
|
||||
default: {
|
||||
CARBON_CHECK(IsValueKindDependent(destination_type))
|
||||
<< "Can't convert value " << *value << " to type "
|
||||
<< *destination_type;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
CARBON_CHECK(tuple->elements().size() ==
|
||||
destination_element_types.size());
|
||||
@@ -767,6 +793,10 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> value,
|
||||
EvalAssociatedConstant(cast<AssociatedConstant>(value), source_loc));
|
||||
if (isa<AssociatedConstant>(value)) {
|
||||
return ProgramError(source_loc)
|
||||
<< "value of associated constant " << *value << " is not known";
|
||||
}
|
||||
return Convert(value, destination_type, source_loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,68 +32,6 @@ using llvm::isa;
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
struct TypeChecker::SingleStepEqualityContext : public EqualityContext {
|
||||
public:
|
||||
SingleStepEqualityContext(Nonnull<const TypeChecker*> type_checker,
|
||||
Nonnull<const ImplScope*> impl_scope)
|
||||
: type_checker_(type_checker), impl_scope_(impl_scope) {}
|
||||
|
||||
// Visits the values that are equal to the given value and a single step away
|
||||
// according to an equality constraint that is either scope or within a final
|
||||
// impl corresponding to an associated constant. Stops and returns `false` if
|
||||
// the visitor returns `false`, otherwise returns `true`.
|
||||
auto VisitEqualValues(Nonnull<const Value*> value,
|
||||
llvm::function_ref<bool(Nonnull<const Value*>)> visitor)
|
||||
const -> bool override {
|
||||
if (type_checker_->trace_stream_) {
|
||||
**type_checker_->trace_stream_ << "looking for values equal to " << *value
|
||||
<< " in\n"
|
||||
<< *impl_scope_;
|
||||
}
|
||||
|
||||
if (!impl_scope_->VisitEqualValues(value, visitor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Also look up and visit the corresponding impl if this is an associated
|
||||
// constant.
|
||||
if (auto* assoc = dyn_cast<AssociatedConstant>(value)) {
|
||||
// Perform an impl lookup to see if we can resolve this constant.
|
||||
// The source location doesn't matter, we're discarding the diagnostics.
|
||||
if (auto* impl_witness = dyn_cast<ImplWitness>(&assoc->witness())) {
|
||||
// Instantiate the impl to find the concrete constraint it implements.
|
||||
Nonnull<const ConstraintType*> constraint =
|
||||
impl_witness->declaration().constraint_type();
|
||||
constraint = cast<ConstraintType>(
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (type_checker_->trace_stream_) {
|
||||
**type_checker_->trace_stream_
|
||||
<< "Could not resolve associated constant " << *assoc << ": "
|
||||
<< "witness " << assoc->witness()
|
||||
<< " depends on a generic parameter\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Nonnull<const TypeChecker*> type_checker_;
|
||||
Nonnull<const ImplScope*> impl_scope_;
|
||||
};
|
||||
|
||||
static void SetValue(Nonnull<Pattern*> pattern, Nonnull<const Value*> value) {
|
||||
// TODO: find some way to CHECK that `value` is identical to pattern->value(),
|
||||
// if it's already set. Unclear if `ValueEqual` is suitable, because it
|
||||
@@ -107,8 +45,7 @@ static void SetValue(Nonnull<Pattern*> pattern, Nonnull<const Value*> value) {
|
||||
auto TypeChecker::IsSameType(Nonnull<const Value*> type1,
|
||||
Nonnull<const Value*> type2,
|
||||
const ImplScope& impl_scope) const -> bool {
|
||||
SingleStepEqualityContext equality_ctx(this, &impl_scope);
|
||||
return TypeEqual(type1, type2, &equality_ctx);
|
||||
return TypeEqual(type1, type2, std::nullopt);
|
||||
}
|
||||
|
||||
auto TypeChecker::ExpectExactType(SourceLocation source_loc,
|
||||
@@ -1223,8 +1160,8 @@ class TypeChecker::ConstraintTypeBuilder {
|
||||
<< "multiple different rewrites for `.("
|
||||
<< *rewrite.interface << "." << *GetName(*rewrite.constant)
|
||||
<< ")`:\n"
|
||||
<< " " << *existing.replacement << "\n"
|
||||
<< " " << *rewrite.replacement;
|
||||
<< " " << existing.replacement->value() << "\n"
|
||||
<< " " << rewrite.replacement->value();
|
||||
}
|
||||
}
|
||||
rewrite_constraints_.push_back(std::move(rewrite));
|
||||
@@ -1340,6 +1277,7 @@ class TypeChecker::ConstraintTypeBuilder {
|
||||
}
|
||||
impl_scope->Add(impl_constraints, llvm::None, llvm::None, GetSelfWitness(),
|
||||
type_checker);
|
||||
// TODO: Bring equality constraints into scope too.
|
||||
}
|
||||
|
||||
// Converts the builder into a ConstraintType. Note that this consumes the
|
||||
@@ -3156,9 +3094,11 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
ImplScope inner_impl_scope;
|
||||
inner_impl_scope.AddParent(&impl_scope);
|
||||
|
||||
auto& self = where.self_binding();
|
||||
ConstraintTypeBuilder builder(arena_, &self);
|
||||
|
||||
// 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<const Value*> base_type,
|
||||
TypeCheckTypeExp(&self.type(), impl_scope));
|
||||
self.set_static_type(base_type);
|
||||
@@ -3170,7 +3110,6 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
base_type));
|
||||
|
||||
// Start with the given constraint.
|
||||
ConstraintTypeBuilder builder(arena_, &self);
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
builder.AddAndSubstitute(*this, base, builder.GetSelfType(),
|
||||
builder.GetSelfWitness(), Bindings(),
|
||||
@@ -3264,6 +3203,16 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
rewrite_clause.source_loc(), {.interface = result.interface,
|
||||
.constant = constant,
|
||||
.replacement = replacement}));
|
||||
// Also find (or add) `.Self is I`, and add `.Self.T == V`.
|
||||
int index = builder.AddImplConstraint(
|
||||
{.type = builder.GetSelfType(), .interface = result.interface});
|
||||
auto* witness =
|
||||
MakeConstraintWitnessAccess(builder.GetSelfWitness(), index);
|
||||
builder.AddEqualityConstraint(
|
||||
{.values = {arena_->New<AssociatedConstant>(
|
||||
builder.GetSelfType(), result.interface,
|
||||
constant, witness),
|
||||
replacement_value}});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3466,6 +3415,13 @@ auto TypeChecker::TypeCheckPattern(
|
||||
}
|
||||
case PatternKind::GenericBinding: {
|
||||
auto& binding = cast<GenericBinding>(*p);
|
||||
|
||||
// The binding can be referred to in its own type via `.Self`, so set up
|
||||
// its symbolic identity before we type-check and interpret the type.
|
||||
auto* val = arena_->New<VariableType>(&binding);
|
||||
binding.set_symbolic_identity(val);
|
||||
SetValue(&binding, val);
|
||||
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> type,
|
||||
TypeCheckTypeExp(&binding.type(), impl_scope));
|
||||
if (expected) {
|
||||
@@ -3479,10 +3435,6 @@ auto TypeChecker::TypeCheckPattern(
|
||||
<< "`.Self` used in type of non-type binding `" << binding.name()
|
||||
<< "`";
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> val,
|
||||
InterpPattern(&binding, arena_, trace_stream_));
|
||||
binding.set_symbolic_identity(val);
|
||||
SetValue(&binding, val);
|
||||
|
||||
// Create an impl binding if we have a constraint.
|
||||
if (isa<ConstraintType, InterfaceType>(type)) {
|
||||
@@ -4477,50 +4429,11 @@ auto TypeChecker::CheckImplIsComplete(Nonnull<const InterfaceType*> iface_type,
|
||||
const auto& iface_decl = iface_type->declaration();
|
||||
for (Nonnull<Declaration*> m : iface_decl.members()) {
|
||||
if (auto* assoc = dyn_cast<AssociatedConstantDeclaration>(m)) {
|
||||
// An associated constant must be given exactly one value.
|
||||
if (LookupRewrite(impl_decl->constraint_type(), iface_type, assoc)) {
|
||||
// OK, named by `=` constraint.
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Remove the rest of this and just reject if there's no `=`.
|
||||
Nonnull<const Value*> expected = arena_->New<AssociatedConstant>(
|
||||
self_type, iface_type, assoc, self_witness);
|
||||
|
||||
bool found_any = false;
|
||||
std::optional<Nonnull<const Value*>> found_value;
|
||||
std::optional<Nonnull<const Value*>> second_value;
|
||||
auto visitor = [&](Nonnull<const Value*> equal_value) {
|
||||
found_any = true;
|
||||
if (!isa<AssociatedConstant>(equal_value)) {
|
||||
if (!found_value ||
|
||||
ValueEqual(equal_value, *found_value, std::nullopt)) {
|
||||
found_value = equal_value;
|
||||
} else {
|
||||
second_value = equal_value;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
impl_decl->constraint_type()->VisitEqualValues(expected, visitor);
|
||||
if (!found_any) {
|
||||
return ProgramError(impl_decl->source_loc())
|
||||
<< "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 {}
|
||||
// ... is insufficient to establish a value for either X or Y.
|
||||
// But perhaps we can allow
|
||||
// impl forall [T:! HasX] T as HasY where .Y == .X {}
|
||||
// An associated constant must be given a value.
|
||||
if (!LookupRewrite(impl_decl->constraint_type(), iface_type, assoc)) {
|
||||
return ProgramError(impl_decl->source_loc())
|
||||
<< "implementation doesn't provide a concrete value for "
|
||||
<< *expected;
|
||||
} else if (second_value) {
|
||||
return ProgramError(impl_decl->source_loc())
|
||||
<< "implementation provides multiple values for " << *expected
|
||||
<< ": " << **found_value << " and " << **second_value;
|
||||
<< *iface_type << "." << assoc->binding().name();
|
||||
}
|
||||
} else if (isa<InterfaceImplDeclaration, InterfaceExtendsDeclaration>(m)) {
|
||||
// These get translated into constraints so there's nothing we need to
|
||||
@@ -4685,22 +4598,28 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
|
||||
self_impl_scope.Add(iface_type, impl_type_value, self_witness, *this);
|
||||
}
|
||||
}
|
||||
// This impl also provides all of its equalities.
|
||||
// TODO: Only the ones from rewrite constraints.
|
||||
for (auto& eq : constraint_type->equality_constraints()) {
|
||||
self_impl_scope.AddEqualityConstraint(&eq);
|
||||
}
|
||||
// Ensure that's enough for our interface to be satisfied.
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
impl_witness, self_impl_scope.Resolve(constraint_type, impl_type_value,
|
||||
impl_decl->source_loc(), *this));
|
||||
}
|
||||
|
||||
// Declare the impl members.
|
||||
ScopeInfo impl_scope_info = ScopeInfo::ForNonClassScope(&impl_scope);
|
||||
// Declare the impl members. An `impl` behaves like a class scope.
|
||||
ScopeInfo impl_scope_info =
|
||||
ScopeInfo::ForClassScope(scope_info, &impl_scope, generic_bindings);
|
||||
for (Nonnull<Declaration*> m : impl_decl->members()) {
|
||||
CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, impl_scope_info));
|
||||
}
|
||||
|
||||
// Create the implied impl bindings.
|
||||
CARBON_RETURN_IF_ERROR(CheckAndAddImplBindings(impl_decl, impl_type_value,
|
||||
self_witness, impl_witness,
|
||||
generic_bindings, scope_info));
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
CheckAndAddImplBindings(impl_decl, impl_type_value, self_witness,
|
||||
impl_witness, generic_bindings, impl_scope_info));
|
||||
|
||||
if (trace_stream_) {
|
||||
**trace_stream_ << "** finished declaring impl " << *impl_decl->impl_type()
|
||||
|
||||
@@ -80,7 +80,6 @@ class TypeChecker {
|
||||
-> Nonnull<const Witness*>;
|
||||
|
||||
private:
|
||||
struct SingleStepEqualityContext;
|
||||
class ConstraintTypeBuilder;
|
||||
class SubstitutedGenericBindings;
|
||||
class ArgumentDeduction;
|
||||
|
||||
@@ -512,7 +512,10 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
break;
|
||||
case Value::Kind::AssociatedConstant: {
|
||||
const auto& assoc = cast<AssociatedConstant>(*this);
|
||||
out << "(" << assoc.base() << ")." << assoc.constant().binding().name();
|
||||
out << "(" << assoc.base() << ").(";
|
||||
PrintNameWithBindings(out, &assoc.interface().declaration(),
|
||||
assoc.interface().args());
|
||||
out << "." << assoc.constant().binding().name() << ")";
|
||||
break;
|
||||
}
|
||||
case Value::Kind::ContinuationValue: {
|
||||
@@ -629,7 +632,7 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
|
||||
return true;
|
||||
}
|
||||
if (t1->kind() != t2->kind()) {
|
||||
if (isa<AssociatedConstant>(t1) || isa<AssociatedConstant>(t2)) {
|
||||
if (IsValueKindDependent(t1) || IsValueKindDependent(t2)) {
|
||||
return ValueEqual(t1, t2, equality_ctx);
|
||||
}
|
||||
return false;
|
||||
@@ -942,7 +945,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
// associated constant; otherwise we should be able to do better by looking
|
||||
// at the structures of the values.
|
||||
if (equality_ctx) {
|
||||
if (isa<AssociatedConstant>(v1)) {
|
||||
if (IsValueKindDependent(v1)) {
|
||||
auto visitor = [&](Nonnull<const Value*> maybe_v2) {
|
||||
return !ValueStructurallyEqual(v2, maybe_v2, equality_ctx);
|
||||
};
|
||||
@@ -950,7 +953,7 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (isa<AssociatedConstant>(v2)) {
|
||||
if (IsValueKindDependent(v2)) {
|
||||
auto visitor = [&](Nonnull<const Value*> maybe_v1) {
|
||||
return !ValueStructurallyEqual(v1, maybe_v1, equality_ctx);
|
||||
};
|
||||
|
||||
@@ -121,6 +121,13 @@ class Value {
|
||||
const Kind kind_;
|
||||
};
|
||||
|
||||
// Returns whether the fully-resolved kind that this value will eventually have
|
||||
// is currently unknown, because it depends on a generic parameter.
|
||||
inline bool IsValueKindDependent(Nonnull<const Value*> type) {
|
||||
return type->kind() == Value::Kind::VariableType ||
|
||||
type->kind() == Value::Kind::AssociatedConstant;
|
||||
}
|
||||
|
||||
// Base class for types holding contextual information by which we can
|
||||
// determine whether values are equal.
|
||||
class EqualityContext {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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: %{not} %{explorer} %s | %{FileCheck-strict} %s
|
||||
// RUN: %{not} %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Iface {
|
||||
let T:! Type;
|
||||
}
|
||||
|
||||
fn F(T:! Iface where .T == i32) {}
|
||||
|
||||
class Good {}
|
||||
class Bad {}
|
||||
external impl Good as Iface where .T = i32 {}
|
||||
external impl Bad as Iface where .T = Bad {}
|
||||
|
||||
fn Main() -> i32 {
|
||||
F(Good);
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_different_type.carbon:[[@LINE+1]]: constraint requires that class Bad == i32, which is not known to be true
|
||||
F(Bad);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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: %{not} %{explorer} %s | %{FileCheck-strict} %s
|
||||
// RUN: %{not} %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Iface {
|
||||
let N:! i32;
|
||||
}
|
||||
|
||||
fn F(T:! Iface where .N == 5) {}
|
||||
|
||||
class Good {}
|
||||
class Bad {}
|
||||
external impl Good as Iface where .N = 5 {}
|
||||
external impl Bad as Iface where .N = 4 {}
|
||||
|
||||
fn Main() -> i32 {
|
||||
F(Good);
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_different_value.carbon:[[@LINE+1]]: constraint requires that 4 == 5, which is not known to be true
|
||||
F(Bad);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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: %{not} %{explorer} %s | %{FileCheck-strict} %s
|
||||
// RUN: %{not} %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Iface {
|
||||
let T:! Type;
|
||||
}
|
||||
|
||||
fn F[T:! Iface where .T == i32](x: T) {}
|
||||
|
||||
class Class {
|
||||
impl as Iface where .T = i32 {}
|
||||
}
|
||||
|
||||
// OK, constraint on `F` rewritten to `T:! Iface where U == i32`, which we can
|
||||
// prove from the constraint on `U`.
|
||||
fn G[U:! Type where .Self == i32, T:! Iface where .T = U](x: T, y: U) {
|
||||
F(x);
|
||||
}
|
||||
|
||||
// Not OK: would require looking through two levels of `==`.
|
||||
fn H[U:! Type where .Self == i32, T:! Iface where .T == U](x: T, y: U) {
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_equal_indirectly.carbon:[[@LINE+1]]: constraint requires that (T).(Iface.T) == i32, which is not known to be true
|
||||
F(x);
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: Class = {};
|
||||
G(x, 0);
|
||||
H(x, 0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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: %{not} %{explorer} %s | %{FileCheck-strict} %s
|
||||
// RUN: %{not} %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Iface {
|
||||
let T:! Type;
|
||||
}
|
||||
|
||||
fn F[T:! Iface where .T == i32](x: T) {}
|
||||
|
||||
fn G[T:! Iface where .T == i32](x: T) {
|
||||
F(x);
|
||||
}
|
||||
|
||||
fn H[T:! Iface](x: T) {
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_equal_to_dependent_type.carbon:[[@LINE+1]]: constraint requires that (T).(Iface.T) == i32, which is not known to be true
|
||||
F(x);
|
||||
}
|
||||
|
||||
class Class {
|
||||
impl as Iface where .T = i32 {}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var x: Class = {};
|
||||
G(x);
|
||||
H(x);
|
||||
return 0;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ interface HasThreeTypes {
|
||||
let C:! Type;
|
||||
}
|
||||
|
||||
// CHECK:STDERR: 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 {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_1.carbon:[[@LINE+1]]: implementation doesn't provide a concrete value for interface HasThreeTypes.B
|
||||
external impl i32 as HasThreeTypes where .A = i32 and .C = i32 {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
|
||||
@@ -14,7 +14,7 @@ interface HasThreeTypes {
|
||||
let C:! Type;
|
||||
}
|
||||
|
||||
// CHECK:STDERR: 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 {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_incomplete_impl_2.carbon:[[@LINE+1]]: implementation doesn't provide a concrete value for interface HasThreeTypes.C
|
||||
external impl i32 as HasThreeTypes where .A = i32 and .B = .C {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
|
||||
@@ -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:STDERR: 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'
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_indirectly_equal.carbon:[[@LINE+1]]: type error in return value: '(T).(A.T)' is not implicitly convertible to '(U).(A.T)'
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ package ExplorerTest api;
|
||||
interface Vector {
|
||||
let Dim:! i32;
|
||||
}
|
||||
external impl (i32, i32, i32) as Vector where .Dim == 3 {}
|
||||
external impl (i32, i32, i32) as Vector where .Dim = 3 {}
|
||||
|
||||
class Point(Scalar:! Type, Dim:! i32) {}
|
||||
|
||||
@@ -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:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_match_in_deduction.carbon:[[@LINE+1]]: mismatch in non-type values, `(V).Dim` != `3`
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_match_in_deduction.carbon:[[@LINE+1]]: mismatch in non-type values, `(V).(Vector.Dim)` != `3`
|
||||
G(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ interface B {
|
||||
}
|
||||
|
||||
class C(T:! Type) {
|
||||
impl as A & B where .TA == i32 and .TB == i32 {
|
||||
impl as A & B where .TA = i32 and .TB = i32 {
|
||||
fn FA() -> i32 {
|
||||
// OK, know that TA is i32 here.
|
||||
let v: Self.(A.TA) = 1;
|
||||
@@ -29,10 +29,8 @@ class C(T:! Type) {
|
||||
// OK, know that TB is i32 here.
|
||||
let v: Self.(B.TB) = 2;
|
||||
// Don't know that TA is i32; it could be specialized.
|
||||
// TODO: We should not accept this.
|
||||
let w: Self.(A.TA) = 3;
|
||||
// TODO: This error is confusing. We should be diagnosing the previous
|
||||
// line because we don't know that `3` can be converted to `Self.(A.TA)`.
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_multi_impl_scoping.carbon:[[@LINE+1]]: type error in return value: '((class C(T = T)).TB).Result' is not implicitly convertible to 'i32'
|
||||
return v + w;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +38,7 @@ class C(T:! Type) {
|
||||
|
||||
external impl C(i32) as B where .TB == () {
|
||||
fn FB() -> () { return (); }
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_multi_impl_scoping.carbon:[[@LINE+1]]: ambiguous implementations of interface B for class C(T = i32)
|
||||
}
|
||||
|
||||
fn Main() -> i32 { return C(i32).FB(); }
|
||||
|
||||
@@ -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:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_multiple_deduction.carbon:[[@LINE+3]]: deduced multiple different values for T:! Type:
|
||||
// CHECK:STDERR: (X).A
|
||||
// CHECK:STDERR: (X).B
|
||||
// CHECK:STDERR: (X).(HasThreeTypes.A)
|
||||
// CHECK:STDERR: (X).(HasThreeTypes.B)
|
||||
F(x.Make());
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ interface HasType {
|
||||
let T:! Type;
|
||||
}
|
||||
|
||||
// CHECK:STDERR: 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} {}
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_overspecified_impl.carbon:[[@LINE+3]]: multiple different rewrites for `.(interface HasType.T)`:
|
||||
// CHECK:STDERR: i32
|
||||
// CHECK:STDERR: {.a: i32}
|
||||
external impl i32 as HasType where .T = i32 and .T = {.a: i32} {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
|
||||
+2
-2
@@ -13,12 +13,12 @@ interface Iface { let N:! i32; }
|
||||
fn PickType(N: i32) -> Type { return i32; }
|
||||
|
||||
fn F[T:! Iface](x: T) -> i32 {
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value.carbon:[[@LINE+1]]: value of associated constant (T).N is not known
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value.carbon:[[@LINE+1]]: value of associated constant (T).(Iface.N) is not known
|
||||
var x: PickType(T.N) = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
impl i32 as Iface where .N == 5 {}
|
||||
impl i32 as Iface where .N = 5 {}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return F(0);
|
||||
|
||||
+1
-1
@@ -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:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon:[[@LINE+1]]: value of associated constant (T).N is not known
|
||||
// CHECK:STDERR: COMPILATION ERROR: {{.*}}/explorer/testdata/assoc_const/fail_unknown_value_specified_in_constraint.carbon:[[@LINE+1]]: value of associated constant (T).(Iface.N) is not known
|
||||
var x: PickType(T.N) = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ class AlmostI32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl i32 as Frob where .Result == AlmostI32 {
|
||||
impl i32 as Frob where .Result = AlmostI32 {
|
||||
fn F[me: Self]() -> AlmostI32 { return {.val = me}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ interface Vector {
|
||||
class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
impl as Vector where .Dim == 2 {}
|
||||
impl as Vector where .Dim = 2 {}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ interface Vector {
|
||||
class Point {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
impl as Vector where .Dim == 2 {}
|
||||
impl as Vector where .Dim = 2 {}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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 | %{FileCheck-strict} %s
|
||||
// RUN: %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK:STDOUT: result: 1
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Container {
|
||||
let Element:! Type;
|
||||
fn Front[me: Self]() -> Element;
|
||||
}
|
||||
|
||||
fn A[T:! Container where .Element = i32](x: T) -> T.Element {
|
||||
return x.Front();
|
||||
}
|
||||
|
||||
fn B[T:! Container where .Element == i32](x: T) -> T.Element {
|
||||
return A(x);
|
||||
}
|
||||
|
||||
external impl (i32, i32) as Container where .Element = i32 {
|
||||
fn Front[me: Self]() -> i32 {
|
||||
let (a: i32, b: i32) = me;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return B((1, 2));
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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 | %{FileCheck-strict} %s
|
||||
// RUN: %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK:STDOUT: result: 1
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface Container {
|
||||
let Element:! Type;
|
||||
fn Front[me: Self]() -> Element;
|
||||
}
|
||||
|
||||
fn A[T:! Container where .Element == i32](x: T) -> T.Element {
|
||||
return x.Front();
|
||||
}
|
||||
|
||||
fn B[T:! Container where .Element = i32](x: T) -> T.Element {
|
||||
return A(x);
|
||||
}
|
||||
|
||||
external impl (i32, i32) as Container where .Element = i32 {
|
||||
fn Front[me: Self]() -> i32 {
|
||||
let (a: i32, b: i32) = me;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return B((1, 2));
|
||||
}
|
||||
+2
-2
@@ -14,12 +14,12 @@ interface Frob {
|
||||
fn F[me: Self]() -> Result;
|
||||
}
|
||||
|
||||
fn Use[T:! Frob where .Result == .Self](x: T) -> T {
|
||||
fn Use[T:! Frob where .Result = .Self](x: T) -> T {
|
||||
var v: T = x.F();
|
||||
return v;
|
||||
}
|
||||
|
||||
impl i32 as Frob where .Result == i32 {
|
||||
impl i32 as Frob where .Result = i32 {
|
||||
fn F[me: Self]() -> i32 { return me + 1; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ fn Use[T:! Frob](x: T) -> T.Result {
|
||||
return v;
|
||||
}
|
||||
|
||||
impl i32 as Frob where .Result == i32 {
|
||||
impl i32 as Frob where .Result = i32 {
|
||||
fn F[me: Self]() -> i32 { return 0; }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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 | %{FileCheck-strict} %s
|
||||
// RUN: %{explorer-trace} %s | %{FileCheck-allow-unmatched} %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK:STDOUT: result: 6
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
// The constraint here resolves to:
|
||||
//
|
||||
// MulWith(T) where <ConstraintSelf>.(MulWith(T).Result) == <ConstraintSelf>
|
||||
//
|
||||
// Note in particular that this involves a member of `MulWith(T)`, so we need
|
||||
// `T` to have a symbolic identity when checking its own type.
|
||||
fn DoMul[T:! MulWith(.Self) where .Result == .Self](x: T, y: T) -> T {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return DoMul(2, 3);
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as AddWith(i32) where .Result == A {
|
||||
external impl A as AddWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n + rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as BitAndWith(i32) where .Result == A {
|
||||
external impl A as BitAndWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n & rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as BitComplement where .Result == A {
|
||||
external impl A as BitComplement where .Result = A {
|
||||
fn Op[me: Self]() -> A { return {.n = ^me.n}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as BitOrWith(i32) where .Result == A {
|
||||
external impl A as BitOrWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n | rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as BitXorWith(i32) where .Result == A {
|
||||
external impl A as BitXorWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n ^ rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as DivWith(i32) where .Result == A {
|
||||
external impl A as DivWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n / rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as LeftShiftWith(i32) where .Result == A {
|
||||
external impl A as LeftShiftWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n << rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as ModWith(i32) where .Result == A {
|
||||
external impl A as ModWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n % rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as MulWith(i32) where .Result == A {
|
||||
external impl A as MulWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n * rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as Negate where .Result == A {
|
||||
external impl A as Negate where .Result = A {
|
||||
fn Op[me: Self]() -> A { return {.n = -me.n}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as RightShiftWith(i32) where .Result == A {
|
||||
external impl A as RightShiftWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n >> rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package ExplorerTest api;
|
||||
|
||||
class A { var n: i32; }
|
||||
|
||||
external impl A as SubWith(i32) where .Result == A {
|
||||
external impl A as SubWith(i32) where .Result = A {
|
||||
fn Op[me: Self](rhs: i32) -> A { return {.n = me.n - rhs}; }
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ interface HasName {
|
||||
let Name:! String;
|
||||
}
|
||||
|
||||
external impl i32 as HasName where .Name == "i32" {}
|
||||
external impl String as HasName where .Name == "String" {}
|
||||
external impl i32 as HasName where .Name = "i32" {}
|
||||
external impl String as HasName where .Name = "String" {}
|
||||
|
||||
fn Main() -> i32 {
|
||||
Print(i32.(HasName.Name));
|
||||
|
||||
Reference in New Issue
Block a user