Intrinsic function with an optional TEAM_TYPE dummy argument

I’m trying to implement team_number(team) in lib/Evaluate/intrinsics.cpp and I’m running into a problem. The issue is how to handle team_type from the intrinsic module iso_fortran_env. I’ll outline the steps I’ve taken so far and then describe the problem I’m having.

I’ve:

  1. Changed the CategorySet in TypePattern TEAM_TYPE from IntType to DerivedType. I’m pretty sure this is the correct thing to do.

  2. Added team_number to the IntrinsicInterface list with an optional TEAM_TYPE. Again, no problem here.

  3. Implemented checking types and kinds of the actual argument against the team_number’s interface. This is pretty straight forward:

case KindCode::teamType:

argOk = semantics::IsTeamType(GetDerivedTypeSpec(type));

The problem occurs when the code tries to check the dummy argument (team) for the case in which the optional actual argument is missing. The normal control flow path that this would take would be to move a characteristics::DummyDataObject (constructed with characteristics::TypeAndShape) onto the dummyArgs list. However, the TypeAndShape constructor calls GetDefaultKind() with a TEAM_TYPE category argument (which is TypeCategory::DerivedType).

Therein lies the problem. The function IntrinsicTypeDefaultKinds::GetDefaultKind(TypeCategory::DerivedType) fails with a hard error because a derived type is not an intrinsic type. At first I was thoroughly confused by the whole type kind thing for a derived type, specifically one from an intrinsic module. Then as a last (of course) resort, I checked the standard more carefully and found Note 16.34 which states that types in intrinsic modules are not themselves intrinsic. Thus IntrinsicTypeDefaultKinds::GetDefaultKind(TypeCategory::DerivedType) should fail.

So the conclusion is that I need help fixing this (for function team_type() and several other intrinsic functions with a dummy argument of TEAM_TYPE). The code could look something like the following (at roughly line 1670 in intrinsics.cpp):

} else if (d.typePattern.kindCode == KindCode::teamType) {

characteristics::TypeAndShape typeAndShape{DynamicType{TypeCategory::Derived}};

dummyArgs.emplace_back(std::string{d.keyword},

characteristics::DummyDataObject{std::move(typeAndShape)});

}

However, there is no constructor for TypeAndShape with a single argument of type TypeCategory::Derived. After staring at many lines of code in flang I’m left wondering if there shouldn’t be?

Thanks,

Craig

Hi Craig,

I think you may be the first having to deal with optional derived type arguments in intrinsic functions. First, I would like to clarify where you are having an issue with IntrinsicTypeDefaultKinds::GetDefaultKind being called with a derived type category after your first three changes.

By “TypeAndShape constructor calls GetDefaultKind()”, you mean that GetDefaultKind is called in the arguments of a TypeAndShape constructor, not inside the TypeAndShape constructor function itself right ?

Is this at: https://github.com/llvm/llvm-project/blob/d8805574c183484f055552855fa82d2e8932415e/flang/lib/Evaluate/intrinsics.cpp#L1666 ?

} else {

// optional argument is absent

CHECK(d.optionality != Optionality::required);

if (d.typePattern.kindCode == KindCode::same) {

dummyArgs.emplace_back(dummyArgs[sameDummyArg.value()]);

} else {

auto category{d.typePattern.categorySet.LeastElement().value()};

characteristics::TypeAndShape typeAndShape{

DynamicType{category**, defaults.GetDefaultKind(category)}};**

dummyArgs.emplace_back(std::string{d.keyword},

characteristics::DummyDataObject{std::move(typeAndShape)});

Looking at the code you propose, the overall idea looks good to me, the issue is that DynamicType cannot be created from the simply the TypeCategory for derived type. A semantics::DerivedTypeSpec that precisely described the derived type specification of a derived type entity needs to be provided (DynamicType must be different for objects of different derived types, there cannot be a single DynamicType for all derived types).

So going that way, I think you would need a semantics::DerivedTypeSpec for the team type. I am not sure there currently is a way to generate such DerivedTypeSpec without a user object (which I think would be your case, since there is no actual argument to get the type from). That is because TEAM_TYPE, like all derived types defined by the standard, are defined by means of Fortran source files (see module/iso_fortran_env.f90 and module/__fortran_builtins.f90), so the compiler has no knowledge of what these derived types actually are without access to these source/module files (For user objects, these module files are made accessible by the user via use statements to iso_fortran_env).

Jean

Hi Jean,

Thanks for responding.

You asked:

By “TypeAndShape constructor calls GetDefaultKind()”, you mean that GetDefaultKind is called in the arguments of a TypeAndShape constructor, not inside the TypeAndShape constructor function itself right?

Correct, this is exactly what I was talking about though perhaps awkwardly worded.

Re.

So going that way, I think you would need a semantics::DerivedTypeSpec for the team type. I am not sure there currently is a way to generate such DerivedTypeSpec without a user object (which I think would be your case, since there is no actual argument to get the type from).

Yeah, I tried to go down this route and reached the same conclusion. If the optional argument is missing there won’t be an actual argument. Kind of painful.

It just doesn’t feel to me like DynamicType is the right approach; we aren’t talking about polymorphic types in iso_fortran_env I don’t think. I had wanted to do something like what happens when d.typePattern.kindCode == KindCode::same, however, as I recall,

dummyArgs.emplace_back(dummyArgs[sameDummyArg.value()])

failed because of a problem with sameDummyArg.value(). If I understand the code correctly, nothing complicated is going on, we are just trying to put the same stupid dummy argument back on the list. Hum, I just looked at this some more and I have some ideas I would like to try.

Thanks again,

Craig

Hi Jean,

Nope, what I tried didn’t work. It looks like there is no current way to construct a characteristics::DummyArgument (of variant characteristics::DummyDataObject) without a TypeAndShape object and ultimately requiring a semantics::DerivedTypeSpec for the team type.

As you said,

TEAM_TYPE, like all derived types defined by the standard, are defined by means of Fortran source files (see module/iso_fortran_env.f90 and module/__fortran_builtins.f90), so the compiler has no knowledge of what these derived types actually are without access to these source/module files (For user objects, these module files are made accessible by the user via use statements to iso_fortran_env).

The frontend will already have seen the iso_fortran_env source file so I’m wondering is special accessor functions could be provided with access to these types?

(Yikes, if TEAM_TYPE is this hard I’m really starting to worry about the array descriptor for coarrays.)

Cheers,

Craig

Hi Craig,

Yes, the frontend should have seen iso_fortran_env if team_number is used, so you are right that it’s possible to get the type from that.

The idea would be to go through the module scopes under the global scope (accessible via context.globalScope(), where context is the SemanticsContext). Then you could look inside the builtin modules for a symbol named “__builtin_team_type”. With that type symbol, you should be able to create a semantics::DerivedTypeSpec, and then a semantics::DerivedTypeSpec (For the DerivedTypeSpec sourceName, usually it would be the source position of the type-spec that relates to the object declaration, but here you can probably just use the one from the type symbol.).

You may want to make the return of this helper function optional and emit an error on the client side in case the modules were not loaded for some reasons, though I am not entirely sure if this can happen.

You should be able to find how to do all that looking at semantics/scope.h, semantics/type.h and the implementation of semantics::IsBuiltinDerivedType (in semantics/tools.cpp). I think it can give you an interesting overview of how the front-end type system, symbols and scopes work, but if you do not want to deal with this part, let me know and I’ll give it a try.

Jean

Hi Jean,

I’m actually happy to do this as long as it makes sense. I’ve been really curious to know about how symbols and scopes work anyway.

An alternative would be to have multiple entries in intrinsics.cpp for the optional team dummy arguments. One entry for no team and one entry for a required team arg. However, this is a bit of a hack. I think I tried this out initially to see if it would work but I actually can’t remember if it fully does.

Whatever, let me know what you all think is the best way to proceed and I’ll muddle through.

Thanks,

Craig

Hi Craig,

Having a different signature in the table is an interesting idea, however it might lead to some trouble in lowering that expects to find the Fortran standard signatures (at least in terms of number of arguments and names).

An alternative would be to allow partial characterization of absent arguments in evaluate::DummyDataObject. After all, it may not always be possible to come with a type and a shape for an absent argument. It is not very clear to me that setting an exact type and shape of absent arguments in the characteristics built for intrinsic procedures (evaluate::characteristics::Procedure) is useful. It might be worth checking if the information is actually needed in this case, and if not, to add a way to indicate that the exact characteristic of a DummyDataObject is unknown, and save the trouble of trying to build a type-spec for it.

Jean

Yikes, I’ve finally had a chance to get back to this and spend enough time to wrap my head around it.

What you suggested seems to work:

An alternative would be to allow partial characterization of absent arguments in evaluate::DummyDataObject. After all, it may not always be possible to come with a type and a shape for an absent argument. It is not very clear to me that setting an exact type and shape of absent arguments in the characteristics built for intrinsic procedures (evaluate::characteristics::Procedure) is useful. It might be worth checking if the information is actually needed in this case, and if not, to add a way to indicate that the exact characteristic of a DummyDataObject is unknown, and save the trouble of trying to build a type-spec for it.

The alternative:

The idea would be to go through the module scopes under the global scope (accessible via context.globalScope(), where context is the SemanticsContext). Then you could look inside the builtin modules for a symbol named “__builtin_team_type”.

is problematic because SemanticsContext isn’t directly available, only FoldingContext.

I need to poke around in the debugger some more to convince myself that missing actual arguments really don’t need to have their optional dummies fully characterized. Then I can resubmit a patch and hopefully move on.

Thanks for helping with this,

Craig