diff --git a/src/java/com/claro/claro_programs/modules/emergent_inheritance_support/test_inheritance.claro b/src/java/com/claro/claro_programs/modules/emergent_inheritance_support/test_inheritance.claro index 3e56735f..b866ebb1 100644 --- a/src/java/com/claro/claro_programs/modules/emergent_inheritance_support/test_inheritance.claro +++ b/src/java/com/claro/claro_programs/modules/emergent_inheritance_support/test_inheritance.claro @@ -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, 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); } diff --git a/src/java/com/claro/intermediate_representation/expressions/procedures/functions/ContractFunctionCallExpr.java b/src/java/com/claro/intermediate_representation/expressions/procedures/functions/ContractFunctionCallExpr.java index a08b0c6c..f7bc280e 100644 --- a/src/java/com/claro/intermediate_representation/expressions/procedures/functions/ContractFunctionCallExpr.java +++ b/src/java/com/claro/intermediate_representation/expressions/procedures/functions/ContractFunctionCallExpr.java @@ -642,7 +642,7 @@ public static boolean resolveContractType( return true; } - private static List getAllDynamicDispatchConcreteContractProcedureNames( + public static List getAllDynamicDispatchConcreteContractProcedureNames( String contractName, ImmutableCollection dynamicDispatchSupportedOverTypeParamIndices, ImmutableList types) { diff --git a/src/java/com/claro/intermediate_representation/expressions/procedures/functions/FunctionCallExpr.java b/src/java/com/claro/intermediate_representation/expressions/procedures/functions/FunctionCallExpr.java index fe983e79..b21382d6 100644 --- a/src/java/com/claro/intermediate_representation/expressions/procedures/functions/FunctionCallExpr.java +++ b/src/java/com/claro/intermediate_representation/expressions/procedures/functions/FunctionCallExpr.java @@ -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 optionalOriginatingDepModuleName; @@ -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, @@ -592,16 +590,35 @@ public static void validateGenericProcedureCall( for (String requiredContract : genericFunctionRequiredContractsMap.keySet()) { for (ImmutableList requiredContractTypeParamNames : genericFunctionRequiredContractsMap.get(requiredContract)) { - ImmutableList.Builder requiredContractConcreteTypesBuilder = ImmutableList.builder(); + ImmutableList.Builder requiredContractConcreteTypesBuilder = ImmutableList.builder(); + ImmutableList.Builder 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 requiredContractConcreteTypes = requiredContractConcreteTypesBuilder.build(); - if (!scopedHeap.isIdentifierDeclared(ContractImplementationStmt.getContractTypeString( - requiredContract, requiredContractConcreteTypes))) { - throw ClaroTypeException.forGenericProcedureCallForConcreteTypesWithRequiredContractImplementationMissing( - procedureName_OUT_PARAM.get(), referencedIdentifierType_OUT_PARAM.get(), requiredContract, requiredContractConcreteTypes); + ImmutableList requiredContractConcreteTypeStrings = + requiredContractConcreteTypeStringsBuilder.build(); + if (!scopedHeap.isIdentifierDeclared( + ContractImplementationStmt.getContractTypeString(requiredContract, requiredContractConcreteTypeStrings))) { + List 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); + } } } }