Skip to content

Commit

Permalink
Support Dynamic Dispatch Over Contract Impls Defined in Dep Modules
Browse files Browse the repository at this point in the history
This enables generic procedures with requires clauses to be called via oneof concrete type params if all combinations are valid contract impls. This allows the monomorphization to defer to the contract's generated dynamic dispatch helper procedure as internally.

This functionality allows Claro's generic procedures to be as expressive as any procedure in a Java-like OO language accepting some super-type and dynamically dispatching to any overridden method on whatever concrete subtype it receives. And *importantly it does this WITHOUT introducing any notion of subtyping to the language*!! +1000 for minimizing complexity.
  • Loading branch information
JasonSteving99 committed Oct 5, 2023
1 parent 5c528c3 commit ff5f1fc
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ greet(dog);
greet(cat);

print("----------------------------------------------------------------------------------------------------");

print("Dynamically dispatching to the contract procedure directly:");
# The below code block is a demonstration of almost the full expressive power of the typical OO-paradigm languages
# within Claro.
var animals: [oneof<Dog::Dog, Cat::Cat>] = [dog, cat];
for (animal in animals) {
# Dynamic dispatch over the animals without using any notion of subtyping.
print(Greeter::Greeter::getGreeting(animal));
# # TODO(steving) Claro needs to relax generic procedure call checks to allow the call to be made in this situation.
# # TODO(steving) This is a complex situation to be sure, but the idea here is that since the concrete type given
# # TODO(steving) for the generic type param `T` is a oneof<> where, for all variants the required contract has been
# # TODO(steving) implemented, then by definition, Claro should allow the call and then the monomorphization will
# # TODO(steving) make use of dynamic dispatch over the oneof<>.
# greet(animal);
}

print("----------------------------------------------------------------------------------------------------");
print("Dynamically dispatching to the contract procedure indirectly through a generic procedure requiring the contract impl:");
# This is a complex situation to be sure, but the idea here is that since the concrete type given
# for the generic type param `T` is a oneof<> where for all variants the required contract has been
# implemented, then by definition, Claro should allow the call and then the monomorphization will
# make use of dynamic dispatch over the oneof<>.
for (animal in animals) {
greet(animal);
}
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ public static boolean resolveContractType(
return true;
}

private static List<String> getAllDynamicDispatchConcreteContractProcedureNames(
public static List<String> getAllDynamicDispatchConcreteContractProcedureNames(
String contractName,
ImmutableCollection<Integer> dynamicDispatchSupportedOverTypeParamIndices,
ImmutableList<Type> types) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
import com.google.common.collect.*;
import com.google.common.hash.Hashing;

import java.util.HashMap;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class FunctionCallExpr extends Expr {
public Optional<String> optionalOriginatingDepModuleName;
Expand Down Expand Up @@ -141,7 +139,7 @@ public Type getValidatedExprType(ScopedHeap scopedHeap) throws ClaroTypeExceptio
? this.name.substring(this.name.lastIndexOf('$') + 1)
+ "$"
+ ScopedHeap.getDefiningModuleDisambiguator(
Optional.of(this.name.substring("$DEP_MODULE$".length(), this.name.lastIndexOf('$'))))
Optional.of(this.name.substring("$DEP_MODULE$" .length(), this.name.lastIndexOf('$'))))
: String.format(
"%s$%s",
this.name,
Expand Down Expand Up @@ -592,16 +590,35 @@ public static void validateGenericProcedureCall(
for (String requiredContract : genericFunctionRequiredContractsMap.keySet()) {
for (ImmutableList<Type> requiredContractTypeParamNames :
genericFunctionRequiredContractsMap.get(requiredContract)) {
ImmutableList.Builder<String> requiredContractConcreteTypesBuilder = ImmutableList.builder();
ImmutableList.Builder<Type> requiredContractConcreteTypesBuilder = ImmutableList.builder();
ImmutableList.Builder<String> requiredContractConcreteTypeStringsBuilder = ImmutableList.builder();
for (Type requiredContractTypeParam : requiredContractTypeParamNames) {
requiredContractConcreteTypesBuilder.add(
genericTypeParamTypeHashMap.get(requiredContractTypeParam).toString());
Type concreteType = genericTypeParamTypeHashMap.get(requiredContractTypeParam);
requiredContractConcreteTypesBuilder.add(concreteType);
requiredContractConcreteTypeStringsBuilder.add(concreteType.toString());
}
ImmutableList<String> requiredContractConcreteTypes = requiredContractConcreteTypesBuilder.build();
if (!scopedHeap.isIdentifierDeclared(ContractImplementationStmt.getContractTypeString(
requiredContract, requiredContractConcreteTypes))) {
throw ClaroTypeException.forGenericProcedureCallForConcreteTypesWithRequiredContractImplementationMissing(
procedureName_OUT_PARAM.get(), referencedIdentifierType_OUT_PARAM.get(), requiredContract, requiredContractConcreteTypes);
ImmutableList<String> requiredContractConcreteTypeStrings =
requiredContractConcreteTypeStringsBuilder.build();
if (!scopedHeap.isIdentifierDeclared(
ContractImplementationStmt.getContractTypeString(requiredContract, requiredContractConcreteTypeStrings))) {
List<String> requiredContractImplsForDynamicDispatchSupport =
ContractFunctionCallExpr.getAllDynamicDispatchConcreteContractProcedureNames(
requiredContract,
// Filter the set of supported dispatch args, based on the actual args having tried to nest a oneof.
IntStream.range(0, requiredContractTypeParamNames.size())
.boxed()
.collect(ImmutableList.toImmutableList()),
requiredContractConcreteTypesBuilder.build()
);
// If it turns out that dynamic dispatch would be possible, then the call to this generic function should be
// allowed so that the function body can just go ahead and codegen dynamic dispatch calls.
boolean dynamicDispatchIsSupportedOverCurrentContract =
!requiredContractImplsForDynamicDispatchSupport.isEmpty()
&& requiredContractImplsForDynamicDispatchSupport.stream().allMatch(scopedHeap::isIdentifierDeclared);
if (!dynamicDispatchIsSupportedOverCurrentContract) {
throw ClaroTypeException.forGenericProcedureCallForConcreteTypesWithRequiredContractImplementationMissing(
procedureName_OUT_PARAM.get(), referencedIdentifierType_OUT_PARAM.get(), requiredContract, requiredContractConcreteTypeStrings);
}
}
}
}
Expand Down

0 comments on commit ff5f1fc

Please sign in to comment.