Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dyno: initial implementation of checking (explicit) interface constraints #26396

Open
wants to merge 72 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
36a7395
Add a class for interface types
DanilaFe Dec 3, 2024
48de29d
Allow empty substitutions
DanilaFe Dec 3, 2024
aba8332
Resolve generic types for interface names
DanilaFe Dec 4, 2024
7c312ef
Expose interface type fields via getters
DanilaFe Dec 4, 2024
e157006
Allow type constructors for interfaces
DanilaFe Dec 4, 2024
cfa3b26
Enable resolving type constructors for interfaces
DanilaFe Dec 4, 2024
453c41f
Factor out some code to help compute implemented interfaces
DanilaFe Dec 5, 2024
536bd0e
Expose inheritance expression computation in header
DanilaFe Dec 5, 2024
c53c55c
Use a specific type for implementation points
DanilaFe Dec 5, 2024
d7bfb3a
Store formals in ImplementationPoints
DanilaFe Dec 5, 2024
550db2c
Add initial implementation of handling Implements statements
DanilaFe Dec 5, 2024
7aaea4a
Collect implementation points for an interface
DanilaFe Dec 5, 2024
9f300f0
Add a basic algorithm for looking for instantiation points
DanilaFe Dec 6, 2024
f5c7c58
Allow constructing an interface with a list of types.
DanilaFe Dec 5, 2024
8a1c629
[WIP] Start on 'primImplementsInterface'
DanilaFe Dec 5, 2024
56db98b
Fix quirks in implementation point search
DanilaFe Dec 6, 2024
c3a2168
Ensure unary `implements` statements work
DanilaFe Dec 6, 2024
f3f7798
Remove type query logic
DanilaFe Dec 6, 2024
cd328c1
Add a comment about disambiguation
DanilaFe Dec 6, 2024
5325061
Add some ID predicates
DanilaFe Dec 6, 2024
a60249b
Add interface frames for outer variable lookup
DanilaFe Dec 6, 2024
b980819
Allow ImplementationPoint to be used in queries
DanilaFe Dec 6, 2024
df6dc9b
Resolve outer variables from interfaces
DanilaFe Dec 6, 2024
49b16d9
Add initial sketch of checking interface constraints
DanilaFe Dec 6, 2024
c107940
Use typedSignatureInitial for interface resolution
DanilaFe Dec 6, 2024
295814f
Add an 'implementation witness' type to contain witness information
DanilaFe Dec 9, 2024
189aad6
Avoid caching unstable frames for interfaces
DanilaFe Dec 9, 2024
b8712e8
Switch to using witnesses for constraint satisfaction
DanilaFe Dec 9, 2024
73f6663
Add a 'placeholder type'
DanilaFe Dec 10, 2024
dc829c8
Use placeholders when resolving interface function signatures
DanilaFe Dec 10, 2024
549ea69
Implement substituting placeholders
DanilaFe Dec 10, 2024
2aba152
Use placeholder to improve checking interface signatures
DanilaFe Dec 10, 2024
b26f9c8
Insert placeholders for type queries
DanilaFe Dec 10, 2024
52254c7
Add stringify implementation
DanilaFe Dec 10, 2024
43c6c2f
Fix hashing unordered maps
DanilaFe Dec 11, 2024
ee5c308
Allow partial substitutions for 'canInstantiate'
DanilaFe Dec 11, 2024
8d5fa96
Use placeholders for AnyType in more cases.
DanilaFe Dec 11, 2024
e5c0f52
Change instantiation search to use the implementation point's scope
DanilaFe Dec 11, 2024
a746226
Remove comment intended for copilot
DanilaFe Dec 11, 2024
371cbb6
Remove debugger breaks
DanilaFe Dec 11, 2024
980e4bc
Resolve some TODOs
DanilaFe Dec 11, 2024
b419108
Emit error messages when unable to find candidate function
DanilaFe Dec 11, 2024
bee49a8
Emit errors for missing associated types
DanilaFe Dec 11, 2024
99ef2a8
Tweak comment
DanilaFe Dec 11, 2024
8843368
Add some tests of interfaces
DanilaFe Dec 12, 2024
abc0b43
Add a test for formal naming
DanilaFe Dec 12, 2024
4ef48b9
Add a test for formal naming
DanilaFe Dec 12, 2024
822644d
Ensure compiler itself builds
DanilaFe Dec 12, 2024
174ec73
Use compatible return values to primitives in production
DanilaFe Dec 12, 2024
19d5142
Fixed signedness mismatches
DanilaFe Dec 12, 2024
449efe3
Use a type map instead of a qualified type map for placeholders
DanilaFe Dec 12, 2024
8f6ccee
Stop iterating over the map for some reason
DanilaFe Dec 12, 2024
82e0d4f
Emit an error when associated type does not return type
DanilaFe Dec 12, 2024
811eee9
Don't store intents for associated types
DanilaFe Dec 12, 2024
7ae8a90
Remove unnecessary qualifier
DanilaFe Dec 12, 2024
47e542f
Compute interface genericity from its substitutions
DanilaFe Dec 12, 2024
1ebf335
Store implementation point types directly instead of re-typing them
DanilaFe Dec 12, 2024
23e8071
Change 'resolveImplementsStmt' to match 'resolveModuleStmt'
DanilaFe Dec 13, 2024
2563cca
Piggyback 'resolveImplementsStmt' off 'resolveModuleStmt'
DanilaFe Dec 13, 2024
8c45d2e
Store interface types in ImplementationPoint and use canPass
DanilaFe Dec 13, 2024
506ffd7
Add initial checking of implementation points in module scope
DanilaFe Dec 13, 2024
9002230
Check all implementation points (including from aggregate decls.)
DanilaFe Dec 13, 2024
ee23b3c
Check for auto-implemented constraints
DanilaFe Dec 13, 2024
b9c1b0c
Define new errors
DanilaFe Dec 13, 2024
aae49da
Fix incorrect bounds check in interface
DanilaFe Dec 13, 2024
be9092f
Emit newly-defined errors when handling interfaces
DanilaFe Dec 13, 2024
8a5a323
Lock down more error cases
DanilaFe Dec 13, 2024
b9c5ba9
Resolve TODO
DanilaFe Dec 13, 2024
3a2ad58
Improve stringify function for interfaces
DanilaFe Dec 13, 2024
c35a956
Move interface types into their own header
DanilaFe Dec 13, 2024
65a4090
Add some comments
DanilaFe Dec 13, 2024
140d6e0
Fix some bugs in the way of normal usage of stdlib interfaces
DanilaFe Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/passes/convert-typed-uast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,18 @@ Type* TConverter::helpConvertType(const types::Type* t) {
case typetags::CPtrType: return helpConvertPtrType(t->toPtrType());
case typetags::HeapBufferType: return helpConvertPtrType(t->toPtrType());

// Interfaces require something clever (creating a constrained generic
// function), and we don't have that yet.
case typetags::InterfaceType:
CHPL_UNIMPL("convert interface type");
return dtUnknown; // TODO

// placeholders only occur in interface resolution and should not be
// reachable
case typetags::PlaceholderType:
INT_FATAL("should not be reachable");
return dtUnknown;

// implementation detail tags (should not be reachable)
case typetags::START_ManageableType:
case typetags::END_ManageableType:
Expand Down
10 changes: 10 additions & 0 deletions frontend/include/chpl/parsing/parsing-queries.h
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,11 @@ bool idIsPrivateDecl(Context* context, ID id);
*/
bool idIsFunction(Context* context, ID id);

/**
Returns true if the ID is an interface
*/
bool idIsInterface(Context* context, ID id);

/**
Returns true if the ID is marked 'extern'.
*/
Expand Down Expand Up @@ -557,6 +562,11 @@ const ID& idToParentId(Context* context, ID id);
*/
ID idToParentFunctionId(Context* context, ID id);

/**
Returns the parent interface ID given an ID.
*/
ID idToParentInterfaceId(Context* context, ID id);

/**
Returns the parent AST node given an AST node
*/
Expand Down
15 changes: 12 additions & 3 deletions frontend/include/chpl/resolution/ResolutionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ResolvedFunction;
class TypedFnSignature;
class UntypedFnSignature;
class MatchingIdsWithName;
class ImplementationWitness;

/**
This class is used to manage stack frames that may be necessary while
Expand Down Expand Up @@ -113,7 +114,7 @@ class ResolutionContext {
when they are destroyed. */
class Frame {
public:
enum Kind { FUNCTION, MODULE, UNKNOWN };
enum Kind { FUNCTION, MODULE, INTERFACE, UNKNOWN };

private:
friend class ResolutionContext;
Expand All @@ -126,6 +127,8 @@ class ResolutionContext {

Resolver* rv_ = nullptr;
const ResolvedFunction* rf_ = nullptr;
const types::InterfaceType* ift_ = nullptr;
const ImplementationWitness* witness_ = nullptr;
int64_t index_ = BASE_FRAME_INDEX;
Store cachedResults_;
Kind kind_ = UNKNOWN;
Expand All @@ -137,6 +140,9 @@ class ResolutionContext {
Frame(const ResolvedFunction* rf, int64_t index)
: rf_(rf), index_(index), kind_(FUNCTION) {
}
Frame(const types::InterfaceType* ift, const ImplementationWitness* witness, int64_t index)
: ift_(ift), witness_(witness), index_(index), kind_(INTERFACE) {
}

public:
~Frame() = default;
Expand All @@ -155,12 +161,14 @@ class ResolutionContext {
}

Resolver* rv() { return rv_; }
const ResolvedFunction* rf() { return rf_; }
const ResolvedFunction* rf() const { return rf_; }
const types::InterfaceType* ift() const { return ift_; }
const ImplementationWitness* witness() const { return witness_; }

bool isEmpty() { return !rv() && !rf(); }
const ID& id() const;
const TypedFnSignature* signature() const;
const ResolutionResultByPostorderID* resolutionById() const;
const types::QualifiedType typeForContainedId(ResolutionContext* rc, const ID& id) const;
bool isUnstable() const;

template <typename T>
Expand All @@ -183,6 +191,7 @@ class ResolutionContext {
Frame baseFrame_;

const Frame* pushFrame(const ResolvedFunction* rf);
const Frame* pushFrame(const types::InterfaceType* t, const ImplementationWitness* witness);
const Frame* pushFrame(Resolver* rv, Frame::Kind kind);
void popFrame(const ResolvedFunction* rf);
void popFrame(Resolver* rv);
Expand Down
11 changes: 11 additions & 0 deletions frontend/include/chpl/resolution/can-pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ CanPassResult canPassScalar(Context* context,
return CanPassResult::canPassScalar(context, actualType, formalType);
}

/* Returns true if, all other things equal, a type with substitutions
'instances' is an instantiation of a type with substitutions 'generics'.

If 'allowMissing' is true, considers missing substitutions in 'generics'
to be "any type". Otherwise, requires that each susbtitution in
instances is matched by an existing substitution in generics. */
bool canInstantiateSubstitutions(Context* context,
const SubstitutionsMap& instances,
const SubstitutionsMap& generics,
bool allowMissing);

/* When trying to combine two kinds, you can't just pick one.
For instance, if any type in the list is a value, the result
should be a value, and if any type in the list is const, the
Expand Down
184 changes: 184 additions & 0 deletions frontend/include/chpl/resolution/interface-types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright 2024 Hewlett Packard Enterprise Development LP
* Other additional copyright holders may be indicated within.
*
* The entirety of this work is licensed under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef CHPL_RESOLUTION_INTERFACE_TYPES_H
#define CHPL_RESOLUTION_INTERFACE_TYPES_H

#include "chpl/framework/ID.h"
#include "chpl/framework/update-functions.h"
#include "chpl/types/InterfaceType.h"
#include "chpl/util/hash.h"

namespace chpl {
namespace resolution {

/*
Represent a resolved form of an 'implements'-like statement, which asserts
that a (possibly-generic) list of types satisfies an interface. All
of the following are transformed into implementation points:

record R : I {}
implements R(I);
R implements I;

The types of the "actuals" (R in the above examples) are stored as substitutions
for the InterfaceType. This means that in the case of a concrete implementation
point, its interface() will be equal to constraint being searched for.
*/
class ImplementationPoint {
private:
// The interface being implemented (instantiated with the types of the arguments)
const types::InterfaceType* interface_;
// The ID of the implementation statement
ID id_;

ImplementationPoint(const types::InterfaceType* interface,
ID id)
: interface_(interface), id_(id) {}

static owned<ImplementationPoint> const&
getImplementationPoint(Context* context,
const types::InterfaceType* interface,
ID id);

public:
static const ImplementationPoint*
get(Context* context, const types::InterfaceType* interface, ID id);

static bool update(owned<ImplementationPoint>& lhs,
owned<ImplementationPoint>& rhs) {
return defaultUpdateOwned(lhs, rhs);
}
bool operator==(const ImplementationPoint& other) const {
return interface_ == other.interface_ &&
id_ == other.id_;
}
bool operator!=(const ImplementationPoint& other) const {
return !(*this == other);
}
void mark(Context* context) const;
void stringify(std::ostream& ss, chpl::StringifyKind stringKind) const;

/* get the (possibly-generic) interface being implemented, which includes
substitutions from the actuals. */
const types::InterfaceType* interface() const { return interface_; }

/* get the ID of the implementation point (statement or inheritance expression) */
const ID& id() const { return id_; }
};

/*
Represents evidence that a particular type or list of types implements
a given interface. This includes:

* associated constraints (e.g., a 'totalOrder' interface requires a
'partialOrder' interface to be satisfied).
* associated types (e.g., a 'collection' interface requires a 'Element'
type to be defined; in the standard library, the 'contextManager' interface
specifies the type of the underlying resource).
* required functions (e.g., a 'hashable' interface requires a 'hash' function
to be defined).
*/
class ImplementationWitness {
public:
using ConstraintMap = std::unordered_map<ID, const ImplementationWitness*>;
using AssociatedTypeMap = types::PlaceholderMap;
using FunctionMap = std::unordered_map<ID, ID>;

private:

ConstraintMap associatedConstraints_;
AssociatedTypeMap associatedTypes_;
FunctionMap requiredFns_;

ImplementationWitness(ConstraintMap associatedConstraints,
AssociatedTypeMap associatedTypes,
FunctionMap requiredFns)
: associatedConstraints_(std::move(associatedConstraints)),
associatedTypes_(std::move(associatedTypes)),
requiredFns_(std::move(requiredFns)) {}

static const owned<ImplementationWitness>&
getImplementationWitness(Context* context,
ConstraintMap associatedConstraints,
AssociatedTypeMap associatedTypes,
FunctionMap requiredFns);

public:
static ImplementationWitness*
get(Context* context,
ConstraintMap associatedConstraints,
AssociatedTypeMap associatedTypes,
FunctionMap requiredFns);

static bool update(owned<ImplementationWitness>& lhs,
owned<ImplementationWitness>& rhs) {
return defaultUpdateOwned(lhs, rhs);
}
bool operator==(const ImplementationWitness& other) const {
return associatedConstraints_ == other.associatedConstraints_ &&
associatedTypes_ == other.associatedTypes_ &&
requiredFns_ == other.requiredFns_;
}
bool operator!=(const ImplementationWitness& other) const {
return !(*this == other);
}
void mark(Context* context) const {
chpl::mark<decltype(associatedTypes_)>{}(context, associatedTypes_);
chpl::mark<decltype(requiredFns_)>{}(context, requiredFns_);
chpl::mark<decltype(associatedConstraints_)>{}(context, associatedConstraints_);
}
void stringify(std::ostream& ss, chpl::StringifyKind stringKind) const;

/** Get the associated constraints. */
const ConstraintMap& associatedConstraints() const {
return associatedConstraints_;
}

/** Get the associated types. */
const AssociatedTypeMap& associatedTypes() const {
return associatedTypes_;
}

/** Get the required functions. */
const FunctionMap& requiredFns() const {
return requiredFns_;
}
};

} // end namespace resolution
} // end namespace chpl


namespace std {

template<> struct hash<chpl::resolution::ImplementationWitness::ConstraintMap> {
size_t operator()(const chpl::resolution::ImplementationWitness::ConstraintMap& key) const {
return chpl::hashUnorderedMap(key);
}
};

template<> struct hash<chpl::resolution::ImplementationWitness::FunctionMap> {
size_t operator()(const chpl::resolution::ImplementationWitness::FunctionMap& key) const {
return chpl::hashUnorderedMap(key);
}
};

}
#endif
11 changes: 11 additions & 0 deletions frontend/include/chpl/resolution/resolution-error-classes-list.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,18 @@ ERROR_CLASS(IncompatibleKinds, types::QualifiedType::Kind, const uast::AstNode*,
ERROR_CLASS(IncompatibleRangeBounds, const uast::Range*, types::QualifiedType, types::QualifiedType)
ERROR_CLASS(IncompatibleTypeAndInit, const uast::AstNode*, const uast::AstNode*, const uast::AstNode*, const types::Type*, const types::Type*)
ERROR_CLASS(IncompatibleYieldTypes, const uast::AstNode*, std::vector<std::tuple<uast::Function::IteratorKind, types::QualifiedType, const resolution::TypedFnSignature*>>)
ERROR_CLASS(InterfaceAmbiguousFn, const types::InterfaceType*, ID, const uast::Function*, std::vector<const resolution::TypedFnSignature*>)
ERROR_CLASS(InterfaceInvalidIntent, const types::InterfaceType*, ID, const resolution::TypedFnSignature*, const resolution::TypedFnSignature*)
ERROR_CLASS(InterfaceMissingAssociatedType, const types::InterfaceType*, ID, const uast::Variable*, resolution::CallInfo, std::vector<resolution::ApplicabilityResult>)
ERROR_CLASS(InterfaceMissingFn, const types::InterfaceType*, ID, const resolution::TypedFnSignature*, resolution::CallInfo, std::vector<resolution::ApplicabilityResult>)
ERROR_CLASS(InterfaceMultipleImplements, const uast::AggregateDecl*, const types::InterfaceType*, ID, ID)
ERROR_CLASS(InterfaceNaryInInherits, const uast::AggregateDecl*, const types::InterfaceType*, ID)
ERROR_CLASS(InterfaceReorderedFnFormals, const types::InterfaceType*, ID, const resolution::TypedFnSignature*, const resolution::TypedFnSignature*)
ERROR_CLASS(InvalidClassCast, const uast::PrimCall*, types::QualifiedType)
ERROR_CLASS(InvalidDomainCall, const uast::FnCall*, std::vector<types::QualifiedType>)
ERROR_CLASS(InvalidImplementsActual, const uast::Implements*, const uast::AstNode*, types::QualifiedType)
ERROR_CLASS(InvalidImplementsArity, const uast::Implements*, const types::InterfaceType*, std::vector<types::QualifiedType>)
ERROR_CLASS(InvalidImplementsInterface, const uast::Implements*, types::QualifiedType)
ERROR_CLASS(InvalidIndexCall, const uast::FnCall*, types::QualifiedType)
ERROR_CLASS(InvalidNewTarget, const uast::New*, types::QualifiedType)
ERROR_CLASS(InvalidParamCast, const uast::AstNode*, types::QualifiedType, types::QualifiedType)
Expand All @@ -71,6 +81,7 @@ ERROR_CLASS(MultipleInheritance, const uast::Class*, const uast::AstNode*, const
ERROR_CLASS(MultipleQuestionArgs, const uast::FnCall*, const uast::AstNode*, const uast::AstNode*)
ERROR_CLASS(NestedClassFieldRef, const uast::TypeDecl*, const uast::TypeDecl*, const uast::AstNode*, ID)
ERROR_CLASS(NoMatchingCandidates, const uast::AstNode*, resolution::CallInfo, std::vector<resolution::ApplicabilityResult>)
ERROR_CLASS(NonClassInheritance, const uast::AggregateDecl*, const uast::AstNode*, const types::Type*)
ERROR_CLASS(NonIterable, const uast::AstNode*, const uast::AstNode*, types::QualifiedType, std::vector<std::tuple<uast::Function::IteratorKind, chpl::resolution::TheseResolutionResult>>)
ERROR_CLASS(NoMatchingEnumValue, const uast::AstNode*, const types::EnumType*, types::QualifiedType)
ERROR_CLASS(NotInModule, const uast::Dot*, ID, UniqueString, ID, bool)
Expand Down
Loading
Loading