diff --git a/flang/lib/parser/grammar.h b/flang/lib/parser/grammar.h index 6c0f1ed09de8..87b85a0fee11 100644 --- a/flang/lib/parser/grammar.h +++ b/flang/lib/parser/grammar.h @@ -894,8 +894,8 @@ TYPE_PARSER("SEQUENCE" >> construct{}) // integer-type-spec , type-param-attr-spec :: type-param-decl-list // R734 type-param-attr-spec -> KIND | LEN TYPE_PARSER(construct{}(integerTypeSpec / ",", - "KIND" >> pure(TypeParamDefStmt::KindOrLength::Kind) || - "LEN" >> pure(TypeParamDefStmt::KindOrLength::Length), + "KIND" >> pure(TypeParamDefStmt::KindOrLen::Kind) || + "LEN" >> pure(TypeParamDefStmt::KindOrLen::Len), "::" >> nonemptyList(Parser{}))) // R733 type-param-decl -> type-param-name [= scalar-int-constant-expr] @@ -1138,6 +1138,8 @@ TYPE_PARSER(construct{}(declarationTypeSpec, optionalBeforeColons(nonemptyList(Parser{})), nonemptyList(entityDecl)) || // PGI-only extension: don't require the colons + // TODO: The standard requires the colons if the entity + // declarations contain initializers. extension(construct{}(declarationTypeSpec, defaulted("," >> nonemptyList(Parser{})), "," >> nonemptyList(entityDecl)))) @@ -2663,6 +2665,7 @@ TYPE_PARSER(maybe("UNIT ="_tok) >> construct{}(fileUnitNumber) || "FORM =" >> construct{}(construct{}( pure(ConnectSpec::CharExpr::Kind::Form), scalarDefaultCharExpr)) || + "IOMSG =" >> construct{}(msgVariable) || "IOSTAT =" >> construct{}(statVariable) || "NEWUNIT =" >> construct{}(construct{}( scalar(integer(variable)))) || @@ -3187,15 +3190,18 @@ TYPE_PARSER( // R1401 main-program -> // [program-stmt] [specification-part] [execution-part] // [internal-subprogram-part] end-program-stmt -// R1402 program-stmt -> PROGRAM program-name TYPE_CONTEXT_PARSER("main program"_en_US, - construct{}(maybe(statement("PROGRAM" >> name)), + construct{}(maybe(statement(Parser{})), specificationPart, executionPart, maybe(internalSubprogramPart), unterminatedStatement(endProgramStmt))) +// R1402 program-stmt -> PROGRAM program-name +TYPE_CONTEXT_PARSER( + "PROGRAM statement"_en_US, construct{}("PROGRAM" >> name)) + // R1403 end-program-stmt -> END [PROGRAM [program-name]] TYPE_CONTEXT_PARSER("END PROGRAM statement"_en_US, - "END" >> construct{}(defaulted("PROGRAM" >> maybe(name)))) + construct{}("END" >> defaulted("PROGRAM" >> maybe(name)))) // R1404 module -> // module-stmt [specification-part] [module-subprogram-part] diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h index 14f7cf328928..944462ba3099 100644 --- a/flang/lib/parser/parse-state.h +++ b/flang/lib/parser/parse-state.h @@ -31,8 +31,6 @@ public: : cooked_{that.cooked_}, p_{that.p_}, limit_{that.limit_}, column_{that.column_}, messages_{*that.cooked_.allSources()}, userState_{that.userState_}, inFixedForm_{that.inFixedForm_}, - enableBackslashEscapesInCharLiterals_{ - that.enableBackslashEscapesInCharLiterals_}, strictConformance_{that.strictConformance_}, warnOnNonstandardUsage_{that.warnOnNonstandardUsage_}, warnOnDeprecatedUsage_{that.warnOnDeprecatedUsage_}, @@ -42,8 +40,6 @@ public: column_{that.column_}, messages_{std::move(that.messages_)}, context_{std::move(that.context_)}, userState_{that.userState_}, inFixedForm_{that.inFixedForm_}, - enableBackslashEscapesInCharLiterals_{ - that.enableBackslashEscapesInCharLiterals_}, strictConformance_{that.strictConformance_}, warnOnNonstandardUsage_{that.warnOnNonstandardUsage_}, warnOnDeprecatedUsage_{that.warnOnDeprecatedUsage_}, @@ -83,14 +79,6 @@ public: return *this; } - bool enableBackslashEscapesInCharLiterals() const { - return enableBackslashEscapesInCharLiterals_; - } - ParseState &set_enableBackslashEscapesInCharLiterals(bool yes) { - enableBackslashEscapesInCharLiterals_ = yes; - return *this; - } - bool strictConformance() const { return strictConformance_; } ParseState &set_strictConformance(bool yes) { strictConformance_ = yes; @@ -179,7 +167,6 @@ private: UserState *userState_{nullptr}; bool inFixedForm_{false}; - bool enableBackslashEscapesInCharLiterals_{true}; bool strictConformance_{false}; bool warnOnNonstandardUsage_{false}; bool warnOnDeprecatedUsage_{false}; diff --git a/flang/lib/parser/parse-tree-visitor.h b/flang/lib/parser/parse-tree-visitor.h index 4400641137f3..4bd78dd7ebe5 100644 --- a/flang/lib/parser/parse-tree-visitor.h +++ b/flang/lib/parser/parse-tree-visitor.h @@ -382,12 +382,6 @@ template void Walk(const Suffix &x, V &visitor) { visitor.Post(x); } } -template void Walk(const TypeAttrSpec::Extends &x, V &visitor) { - if (visitor.Pre(x)) { - Walk(x.name, visitor); - visitor.Post(x); - } -} template void Walk(const TypeBoundProcedureStmt::WithInterface &x, V &visitor) { if (visitor.Pre(x)) { @@ -1524,6 +1518,9 @@ template void Walk(const PointerStmt &x, V &visitor) { template void Walk(const Program &x, V &visitor) { WalkWrapperClass(x, visitor); } +template void Walk(const ProgramStmt &x, V &visitor) { + WalkWrapperClass(x, visitor); +} template void Walk(const ProtectedStmt &x, V &visitor) { WalkWrapperClass(x, visitor); } @@ -1551,6 +1548,9 @@ template void Walk(const SyncMemoryStmt &x, V &visitor) { template void Walk(const TargetStmt &x, V &visitor) { WalkWrapperClass(x, visitor); } +template void Walk(const TypeAttrSpec::Extends &x, V &visitor) { + WalkWrapperClass(x, visitor); +} template void Walk(const TypeParamInquiry &x, V &visitor) { WalkWrapperClass(x, visitor); } diff --git a/flang/lib/parser/parse-tree.cc b/flang/lib/parser/parse-tree.cc index c63dcf4c1844..aeefcce84eb3 100644 --- a/flang/lib/parser/parse-tree.cc +++ b/flang/lib/parser/parse-tree.cc @@ -311,6 +311,29 @@ std::ostream &operator<<(std::ostream &o, const FormatSpecification &x) { << ')'; } +#define NESTED_ENUM_FORMATTER(T) \ + NESTED_ENUM_TO_STRING(T) \ + std::ostream &operator<<(std::ostream &o, const T &x) { \ + return o << ToString(x); \ + } + +NESTED_ENUM_FORMATTER(DefinedOperator::IntrinsicOperator) // R608 +NESTED_ENUM_FORMATTER(TypeParamDefStmt::KindOrLen) // R734 +NESTED_ENUM_FORMATTER(AccessSpec::Kind) // R807 +NESTED_ENUM_FORMATTER(IntentSpec::Intent) // R826 +NESTED_ENUM_FORMATTER(ImplicitStmt::ImplicitNoneNameSpec) // R866 +NESTED_ENUM_FORMATTER(ImportStmt::Kind) // R867 +NESTED_ENUM_FORMATTER(StopStmt::Kind) // R1160, R1161 +NESTED_ENUM_FORMATTER(ConnectSpec::CharExpr::Kind) // R1205 +NESTED_ENUM_FORMATTER(IoControlSpec::CharExpr::Kind) // R1213 +NESTED_ENUM_FORMATTER(InquireSpec::CharVar::Kind) // R1231 +NESTED_ENUM_FORMATTER(InquireSpec::IntVar::Kind) // R1231 +NESTED_ENUM_FORMATTER(InquireSpec::LogVar::Kind) // R1231 +NESTED_ENUM_FORMATTER(UseStmt::ModuleNature) // R1410 +NESTED_ENUM_FORMATTER(ProcedureStmt::Kind) // R1506 + +#undef NESTED_ENUM_FORMATTER + // Wrapper class formatting #define WRAPPER_FORMATTER(TYPE) \ std::ostream &operator<<(std::ostream &o, const TYPE &x) { \ @@ -327,6 +350,7 @@ WRAPPER_FORMATTER(IntegerTypeSpec) // R705 WRAPPER_FORMATTER(KindSelector) // R706 WRAPPER_FORMATTER(HollerithLiteralConstant) // extension WRAPPER_FORMATTER(LogicalLiteralConstant) // R725 +WRAPPER_FORMATTER(TypeAttrSpec::Extends) // R728 WRAPPER_FORMATTER(EndTypeStmt) // R730 WRAPPER_FORMATTER(Pass) // R742 & R752 WRAPPER_FORMATTER(FinalProcedureStmt) // R753 @@ -334,12 +358,14 @@ WRAPPER_FORMATTER(ComponentDataSource) // R758 WRAPPER_FORMATTER(EnumeratorDefStmt) // R761 WRAPPER_FORMATTER(BOZLiteralConstant) // R764, R765, R766, R767 WRAPPER_FORMATTER(ArrayConstructor) // R769 +WRAPPER_FORMATTER(AccessSpec) // R807 WRAPPER_FORMATTER(LanguageBindingSpec) // R808 & R1528 WRAPPER_FORMATTER(DeferredCoshapeSpecList) // R810 WRAPPER_FORMATTER(AssumedShapeSpec) // R819 WRAPPER_FORMATTER(DeferredShapeSpecList) // R820 WRAPPER_FORMATTER(AssumedImpliedSpec) // R821 WRAPPER_FORMATTER(ImpliedShapeSpec) // R823 & R824 +WRAPPER_FORMATTER(IntentSpec) // R826 WRAPPER_FORMATTER(AllocatableStmt) // R829 WRAPPER_FORMATTER(AsynchronousStmt) // R831 WRAPPER_FORMATTER(CodimensionStmt) // R834 @@ -413,6 +439,7 @@ WRAPPER_FORMATTER(IdVariable) // R1214 WRAPPER_FORMATTER(WaitStmt) // R1222 WRAPPER_FORMATTER(IdExpr) // R1223 & R1231 WRAPPER_FORMATTER(FormatStmt) // R1301 +WRAPPER_FORMATTER(ProgramStmt) // R1402 WRAPPER_FORMATTER(EndProgramStmt) // R1403 WRAPPER_FORMATTER(ModuleStmt) // R1405 WRAPPER_FORMATTER(EndModuleStmt) // R1406 @@ -495,32 +522,6 @@ EMPTY_TYPE_FORMATTER(Map::EndMapStmt) #undef EMPTY_TYPE_FORMATTER -// R609 -std::ostream &operator<<( - std::ostream &o, DefinedOperator::IntrinsicOperator x) { - switch (x) { - case DefinedOperator::IntrinsicOperator::Power: return o << "Power"; - case DefinedOperator::IntrinsicOperator::Multiply: return o << "Multiply"; - case DefinedOperator::IntrinsicOperator::Divide: return o << "Divide"; - case DefinedOperator::IntrinsicOperator::Add: return o << "Add"; - case DefinedOperator::IntrinsicOperator::Subtract: return o << "Subtract"; - case DefinedOperator::IntrinsicOperator::Concat: return o << "Concat"; - case DefinedOperator::IntrinsicOperator::LT: return o << "LT"; - case DefinedOperator::IntrinsicOperator::LE: return o << "LE"; - case DefinedOperator::IntrinsicOperator::EQ: return o << "EQ"; - case DefinedOperator::IntrinsicOperator::NE: return o << "NE"; - case DefinedOperator::IntrinsicOperator::GE: return o << "GE"; - case DefinedOperator::IntrinsicOperator::GT: return o << "GT"; - case DefinedOperator::IntrinsicOperator::NOT: return o << "NOT"; - case DefinedOperator::IntrinsicOperator::AND: return o << "AND"; - case DefinedOperator::IntrinsicOperator::OR: return o << "OR"; - case DefinedOperator::IntrinsicOperator::EQV: return o << "EQV"; - case DefinedOperator::IntrinsicOperator::NEQV: return o << "NEQV"; - default: CRASH_NO_CASE; - } - return o; -} - // R703 std::ostream &operator<<(std::ostream &o, const DeclarationTypeSpec::Type &x) { return o << "(DeclarationTypeSpec TYPE " << x.derived << ')'; @@ -602,22 +603,6 @@ std::ostream &operator<<( return o << "(LengthAndKind " << x.length << ' ' << x.kind << ')'; } -// R728 type-attr-spec -std::ostream &operator<<(std::ostream &o, const TypeAttrSpec::Extends &x) { - return o << "(Extends " << x.name << ')'; -} - -// R734 type-param-attr-spec -std::ostream &operator<<( - std::ostream &o, const TypeParamDefStmt::KindOrLength &x) { - switch (x) { - case TypeParamDefStmt::KindOrLength::Kind: o << "Kind"; break; - case TypeParamDefStmt::KindOrLength::Length: o << "Length"; break; - default: CRASH_NO_CASE; - } - return o; -} - // R749 type-bound-procedure-stmt std::ostream &operator<<( std::ostream &o, const TypeBoundProcedureStmt::WithoutInterface &x) { @@ -636,27 +621,6 @@ std::ostream &operator<<(std::ostream &o, const AcSpec &x) { return o << "(AcSpec " << x.type << ' ' << x.values << ')'; } -// R807 access-spec -std::ostream &operator<<(std::ostream &o, const AccessSpec &x) { - switch (x.v) { - case AccessSpec::Kind::Public: return o << "(Public)"; - case AccessSpec::Kind::Private: return o << "(Private)"; - default: CRASH_NO_CASE; - } - return o; -} - -// R826 intent-spec -std::ostream &operator<<(std::ostream &o, const IntentSpec &x) { - switch (x.v) { - case IntentSpec::Intent::In: return o << "(Intent In)"; - case IntentSpec::Intent::Out: return o << "(Intent Out)"; - case IntentSpec::Intent::InOut: return o << "(Intent InOut)"; - default: CRASH_NO_CASE; - } - return o; -} - // R863 implicit-stmt std::ostream &operator<<(std::ostream &o, const ImplicitStmt &x) { o << "(ImplicitStmt "; @@ -664,21 +628,10 @@ std::ostream &operator<<(std::ostream &o, const ImplicitStmt &x) { x.u)) { o << "NONE "; } - std::visit([&o](auto &&y) { o << y; }, x.u); + std::visit([&o](const auto &y) { o << y; }, x.u); return o << ')'; } -// R866 -std::ostream &operator<<( - std::ostream &o, ImplicitStmt::ImplicitNoneNameSpec x) { - switch (x) { - case ImplicitStmt::ImplicitNoneNameSpec::External: return o << "External"; - case ImplicitStmt::ImplicitNoneNameSpec::Type: return o << "Type"; - default: CRASH_NO_CASE; - } - return o; -} - // R867 ImportStmt::ImportStmt(Kind &&k, std::list &&n) : kind{k}, names(std::move(n)) { @@ -687,14 +640,10 @@ ImportStmt::ImportStmt(Kind &&k, std::list &&n) std::ostream &operator<<(std::ostream &o, const ImportStmt &x) { o << "(ImportStmt "; - switch (x.kind) { - case ImportStmt::Kind::Default: return o << x.names << ')'; - case ImportStmt::Kind::Only: return o << "Only " << x.names << ')'; - case ImportStmt::Kind::None: return o << "None)"; - case ImportStmt::Kind::All: return o << "All)"; - default: CRASH_NO_CASE; + if (x.kind != ImportStmt::Kind::Default) { + o << x.kind; } - return o; + return o << x.names << ')'; } // R901 designator @@ -871,95 +820,6 @@ std::ostream &operator<<(std::ostream &o, const CaseValueRange::Range &x) { return o << "(Range " << x.lower << ' ' << x.upper << ')'; } -// R1205 -std::ostream &operator<<(std::ostream &o, const ConnectSpec::CharExpr::Kind x) { - switch (x) { - case ConnectSpec::CharExpr::Kind::Access: o << "Access"; break; - case ConnectSpec::CharExpr::Kind::Action: o << "Action"; break; - case ConnectSpec::CharExpr::Kind::Asynchronous: o << "Asynchronous"; break; - case ConnectSpec::CharExpr::Kind::Blank: o << "Blank"; break; - case ConnectSpec::CharExpr::Kind::Decimal: o << "Decimal"; break; - case ConnectSpec::CharExpr::Kind::Delim: o << "Delim"; break; - case ConnectSpec::CharExpr::Kind::Encoding: o << "Encoding"; break; - case ConnectSpec::CharExpr::Kind::Form: o << "Form"; break; - case ConnectSpec::CharExpr::Kind::Pad: o << "Pad"; break; - case ConnectSpec::CharExpr::Kind::Position: o << "Position"; break; - case ConnectSpec::CharExpr::Kind::Round: o << "Round"; break; - case ConnectSpec::CharExpr::Kind::Sign: o << "Sign"; break; - case ConnectSpec::CharExpr::Kind::Dispose: o << "Dispose"; break; - default: CRASH_NO_CASE; - } - return o; -} - -// R1213 -std::ostream &operator<<(std::ostream &o, IoControlSpec::CharExpr::Kind x) { - switch (x) { - case IoControlSpec::CharExpr::Kind::Advance: o << "Advance"; break; - case IoControlSpec::CharExpr::Kind::Blank: o << "Blank"; break; - case IoControlSpec::CharExpr::Kind::Decimal: o << "Decimal"; break; - case IoControlSpec::CharExpr::Kind::Delim: o << "Delim"; break; - case IoControlSpec::CharExpr::Kind::Pad: o << "Pad"; break; - case IoControlSpec::CharExpr::Kind::Round: o << "Round"; break; - case IoControlSpec::CharExpr::Kind::Sign: o << "Sign"; break; - default: CRASH_NO_CASE; - } - return o; -} - -// R1231 -std::ostream &operator<<(std::ostream &o, const InquireSpec::CharVar::Kind x) { - switch (x) { - case InquireSpec::CharVar::Kind::Access: o << "Access"; break; - case InquireSpec::CharVar::Kind::Action: o << "Action"; break; - case InquireSpec::CharVar::Kind::Asynchronous: o << "Asynchronous"; break; - case InquireSpec::CharVar::Kind::Blank: o << "Blank"; break; - case InquireSpec::CharVar::Kind::Decimal: o << "Decimal"; break; - case InquireSpec::CharVar::Kind::Delim: o << "Delim"; break; - case InquireSpec::CharVar::Kind::Encoding: o << "Encoding"; break; - case InquireSpec::CharVar::Kind::Form: o << "Form"; break; - case InquireSpec::CharVar::Kind::Formatted: o << "Formatted"; break; - case InquireSpec::CharVar::Kind::Iomsg: o << "Iomsg"; break; - case InquireSpec::CharVar::Kind::Name: o << "Name"; break; - case InquireSpec::CharVar::Kind::Pad: o << "Pad"; break; - case InquireSpec::CharVar::Kind::Position: o << "Position"; break; - case InquireSpec::CharVar::Kind::Read: o << "Read"; break; - case InquireSpec::CharVar::Kind::Readwrite: o << "Readwrite"; break; - case InquireSpec::CharVar::Kind::Round: o << "Round"; break; - case InquireSpec::CharVar::Kind::Sequential: o << "Sequential"; break; - case InquireSpec::CharVar::Kind::Sign: o << "Sign"; break; - case InquireSpec::CharVar::Kind::Stream: o << "Stream"; break; - case InquireSpec::CharVar::Kind::Status: o << "Status"; break; - case InquireSpec::CharVar::Kind::Write: o << "Write"; break; - default: CRASH_NO_CASE; - } - return o; -} - -std::ostream &operator<<(std::ostream &o, const InquireSpec::IntVar::Kind x) { - switch (x) { - case InquireSpec::IntVar::Kind::Iostat: o << "Iostat"; break; - case InquireSpec::IntVar::Kind::Nextrec: o << "Nextrec"; break; - case InquireSpec::IntVar::Kind::Number: o << "Number"; break; - case InquireSpec::IntVar::Kind::Pos: o << "Pos"; break; - case InquireSpec::IntVar::Kind::Recl: o << "Recl"; break; - case InquireSpec::IntVar::Kind::Size: o << "Size"; break; - default: CRASH_NO_CASE; - } - return o; -} - -std::ostream &operator<<(std::ostream &o, const InquireSpec::LogVar::Kind x) { - switch (x) { - case InquireSpec::LogVar::Kind::Exist: o << "Exist"; break; - case InquireSpec::LogVar::Kind::Named: o << "Named"; break; - case InquireSpec::LogVar::Kind::Opened: o << "Opened"; break; - case InquireSpec::LogVar::Kind::Pending: o << "Pending"; break; - default: CRASH_NO_CASE; - } - return o; -} - // R1307 data-edit-desc (part 1 of 2) std::ostream &operator<<(std::ostream &o, const IntrinsicTypeDataEditDesc &x) { o << "(IntrinsicTypeDataEditDesc "; @@ -982,16 +842,6 @@ std::ostream &operator<<(std::ostream &o, const IntrinsicTypeDataEditDesc &x) { return o << x.width << ' ' << x.digits << ' ' << x.exponentWidth << ')'; } -// R1160, R1161 -std::ostream &operator<<(std::ostream &o, StopStmt::Kind x) { - switch (x) { - case StopStmt::Kind::Stop: o << "Stop"; break; - case StopStmt::Kind::ErrorStop: o << "ErrorStop"; break; - default: CRASH_NO_CASE; - } - return o; -} - // R1210 read-stmt std::ostream &operator<<(std::ostream &o, const ReadStmt &x) { return o << "(ReadStmt " << x.iounit << ' ' << x.format << ' ' << x.controls @@ -1045,19 +895,9 @@ std::ostream &operator<<(std::ostream &o, const FormatItem &x) { return o << ')'; } -// R1409, R1410 +// R1409 std::ostream &operator<<(std::ostream &o, const UseStmt &x) { - o << "(UseStmt "; - if (x.nature) { - switch (*x.nature) { - case UseStmt::ModuleNature::Intrinsic: o << "Intrinsic"; break; - case UseStmt::ModuleNature::Non_Intrinsic: o << "Non_Intrinsic"; break; - default: CRASH_NO_CASE; - } - } else { - o << "()"; - } - o << ' ' << x.moduleName << ' '; + o << "(UseStmt " << x.nature << ' ' << x.moduleName << ' '; std::visit( visitors{ [&o](const std::list &y) -> void { o << "RENAME " << y; }, @@ -1068,15 +908,6 @@ std::ostream &operator<<(std::ostream &o, const UseStmt &x) { } // R1506 -std::ostream &operator<<(std::ostream &o, const ProcedureStmt::Kind &x) { - switch (x) { - case ProcedureStmt::Kind::ModuleProcedure: return o << "ModuleProcedure"; - case ProcedureStmt::Kind::Procedure: return o << "Procedure"; - default: CRASH_NO_CASE; - } - return o; -} - std::ostream &operator<<(std::ostream &o, const ProcedureStmt &x) { return o << "(ProcedureStmt " << std::get<0>(x.t) << ' ' << std::get<1>(x.t) << ')'; diff --git a/flang/lib/parser/parse-tree.h b/flang/lib/parser/parse-tree.h index 011ba1ff5797..a6c300b550c9 100644 --- a/flang/lib/parser/parse-tree.h +++ b/flang/lib/parser/parse-tree.h @@ -82,6 +82,14 @@ WRAPPER_CLASS_BOILERPLATE(classname, type); \ } +// Enumeration types in classes can be defined with this macro, +// which also captures the names of the enums for formatting. +// Invocations an be followed by declarators and must be followed by +// a semicolon. +#define DEFINE_NESTED_ENUM_CLASS(ENUMTYPE, ...) \ + static constexpr const char *ENUMTYPE##AsString{#__VA_ARGS__}; \ + enum class ENUMTYPE { __VA_ARGS__ } + namespace Fortran { namespace parser { @@ -477,30 +485,16 @@ WRAPPER_CLASS(NamedConstant, Name); // The Name here is stored without the dots; e.g., FOO, not .FOO. WRAPPER_CLASS(DefinedOpName, Name); +// R608 intrinsic-operator -> +// ** | * | / | + | - | // | .LT. | .LE. | .EQ. | .NE. | .GE. | .GT. | +// .NOT. | .AND. | .OR. | .EQV. | .NEQV. // R609 defined-operator -> // defined-unary-op | defined-binary-op | extended-intrinsic-op // R610 extended-intrinsic-op -> intrinsic-operator struct DefinedOperator { UNION_CLASS_BOILERPLATE(DefinedOperator); - enum class IntrinsicOperator { // R608 intrinsic-operator - Power, - Multiply, - Divide, - Add, - Subtract, - Concat, - LT, - LE, - EQ, - NE, - GE, - GT, - NOT, - AND, - OR, - EQV, - NEQV - }; + DEFINE_NESTED_ENUM_CLASS(IntrinsicOperator, Power, Multiply, Divide, Add, + Subtract, Concat, LT, LE, EQ, NE, GE, GT, NOT, AND, OR, EQV, NEQV); std::variant u; }; @@ -512,7 +506,7 @@ using ObjectName = Name; // IMPORT , ONLY : import-name-list | IMPORT , NONE | IMPORT , ALL struct ImportStmt { BOILERPLATE(ImportStmt); - enum class Kind { Default, Only, None, All } kind{Kind::Default}; + DEFINE_NESTED_ENUM_CLASS(Kind, Default, Only, None, All) kind{Kind::Default}; ImportStmt(Kind &&k) : kind{k} {} ImportStmt(std::list &&n) : names(std::move(n)) {} ImportStmt(Kind &&, std::list &&); @@ -552,7 +546,7 @@ WRAPPER_CLASS(IntegerTypeSpec, std::optional); // R723 char-length -> ( type-param-value ) | digit-string struct CharLength { UNION_CLASS_BOILERPLATE(CharLength); - std::variant u; + std::variant u; }; // R722 length-selector -> ( [LEN =] type-param-value ) | * char-length [,] @@ -782,7 +776,7 @@ struct ConstantValue { // R807 access-spec -> PUBLIC | PRIVATE struct AccessSpec { - enum class Kind { Public, Private }; + DEFINE_NESTED_ENUM_CLASS(Kind, Public, Private); WRAPPER_CLASS_BOILERPLATE(AccessSpec, Kind); }; @@ -792,11 +786,7 @@ EMPTY_CLASS(Abstract); struct TypeAttrSpec { UNION_CLASS_BOILERPLATE(TypeAttrSpec); EMPTY_CLASS(BindC); - struct Extends { - BOILERPLATE(Extends); - Extends(Name &&n) : name(std::move(n)) {} - Name name; - }; + WRAPPER_CLASS(Extends, Name); std::variant u; }; @@ -830,9 +820,9 @@ struct TypeParamDecl { // integer-type-spec , type-param-attr-spec :: type-param-decl-list // R734 type-param-attr-spec -> KIND | LEN struct TypeParamDefStmt { - enum class KindOrLength { Kind, Length }; // R734 + DEFINE_NESTED_ENUM_CLASS(KindOrLen, Kind, Len); // R734 TUPLE_CLASS_BOILERPLATE(TypeParamDefStmt); - std::tuple> t; + std::tuple> t; }; // R1028 specification-expr -> scalar-int-expr @@ -1209,7 +1199,7 @@ struct ArraySpec { // R826 intent-spec -> IN | OUT | INOUT struct IntentSpec { - enum class Intent { In, Out, InOut }; + DEFINE_NESTED_ENUM_CLASS(Intent, In, Out, InOut); WRAPPER_CLASS_BOILERPLATE(IntentSpec, Intent); }; @@ -1445,8 +1435,7 @@ struct ImplicitSpec { // R866 implicit-name-spec -> EXTERNAL | TYPE struct ImplicitStmt { UNION_CLASS_BOILERPLATE(ImplicitStmt); - enum class ImplicitNoneNameSpec { External, Type }; // R866 - friend std::ostream &operator<<(std::ostream &, ImplicitNoneNameSpec); + DEFINE_NESTED_ENUM_CLASS(ImplicitNoneNameSpec, External, Type); // R866 std::variant, std::list> u; }; @@ -2332,7 +2321,7 @@ struct StopCode { // R1161 error-stop-stmt -> // ERROR STOP [stop-code] [, QUIET = scalar-logical-expr] struct StopStmt { - enum class Kind { Stop, ErrorStop }; + DEFINE_NESTED_ENUM_CLASS(Kind, Stop, ErrorStop); TUPLE_CLASS_BOILERPLATE(StopStmt); std::tuple, std::optional> t; }; @@ -2448,28 +2437,16 @@ WRAPPER_CLASS(ErrLabel, Label); struct ConnectSpec { UNION_CLASS_BOILERPLATE(ConnectSpec); struct CharExpr { - enum class Kind { - Access, - Action, - Asynchronous, - Blank, - Decimal, - Delim, - Encoding, - Form, - Pad, - Position, - Round, - Sign, - Dispose /*extension*/ - }; + DEFINE_NESTED_ENUM_CLASS(Kind, Access, Action, Asynchronous, Blank, Decimal, + Delim, Encoding, Form, Pad, Position, Round, Sign, + Dispose /*extension*/); TUPLE_CLASS_BOILERPLATE(CharExpr); std::tuple t; }; WRAPPER_CLASS(Recl, ScalarIntExpr); WRAPPER_CLASS(Newunit, ScalarIntVariable); - std::variant + std::variant u; }; @@ -2517,7 +2494,8 @@ WRAPPER_CLASS(EorLabel, Label); struct IoControlSpec { UNION_CLASS_BOILERPLATE(IoControlSpec); struct CharExpr { - enum class Kind { Advance, Blank, Decimal, Delim, Pad, Round, Sign }; + DEFINE_NESTED_ENUM_CLASS( + Kind, Advance, Blank, Decimal, Delim, Pad, Round, Sign); TUPLE_CLASS_BOILERPLATE(CharExpr); std::tuple t; }; @@ -2680,39 +2658,19 @@ struct FlushStmt { struct InquireSpec { UNION_CLASS_BOILERPLATE(InquireSpec); struct CharVar { - enum class Kind { - Access, - Action, - Asynchronous, - Blank, - Decimal, - Delim, - Encoding, - Form, - Formatted, - Iomsg, - Name, - Pad, - Position, - Read, - Readwrite, - Round, - Sequential, - Sign, - Stream, - Status, - Write - }; + DEFINE_NESTED_ENUM_CLASS(Kind, Access, Action, Asynchronous, Blank, Decimal, + Delim, Encoding, Form, Formatted, Iomsg, Name, Pad, Position, Read, + Readwrite, Round, Sequential, Sign, Stream, Status, Write); TUPLE_CLASS_BOILERPLATE(CharVar); std::tuple t; }; struct IntVar { - enum class Kind { Iostat, Nextrec, Number, Pos, Recl, Size }; + DEFINE_NESTED_ENUM_CLASS(Kind, Iostat, Nextrec, Number, Pos, Recl, Size); TUPLE_CLASS_BOILERPLATE(IntVar); std::tuple t; }; struct LogVar { - enum class Kind { Exist, Named, Opened, Pending }; + DEFINE_NESTED_ENUM_CLASS(Kind, Exist, Named, Opened, Pending); TUPLE_CLASS_BOILERPLATE(LogVar); std::tuple>> t; }; @@ -2736,17 +2694,20 @@ struct InquireStmt { // R1301 format-stmt -> FORMAT format-specification WRAPPER_CLASS(FormatStmt, FormatSpecification); +// R1402 program-stmt -> PROGRAM program-name +WRAPPER_CLASS(ProgramStmt, Name); + // R1403 end-program-stmt -> END [PROGRAM [program-name]] WRAPPER_CLASS(EndProgramStmt, std::optional); // R1401 main-program -> // [program-stmt] [specification-part] [execution-part] // [internal-subprogram-part] end-program-stmt -// R1402 program-stmt -> PROGRAM program-name struct MainProgram { TUPLE_CLASS_BOILERPLATE(MainProgram); - std::tuple>, SpecificationPart, ExecutionPart, - std::optional, Statement> + std::tuple>, SpecificationPart, + ExecutionPart, std::optional, + Statement> t; }; @@ -2882,7 +2843,7 @@ struct Only { // R1410 module-nature -> INTRINSIC | NON_INTRINSIC struct UseStmt { BOILERPLATE(UseStmt); - enum class ModuleNature { Intrinsic, Non_Intrinsic }; // R1410 + DEFINE_NESTED_ENUM_CLASS(ModuleNature, Intrinsic, Non_Intrinsic); // R1410 template UseStmt(std::optional &&nat, Name &&n, std::list &&x) : nature(std::move(nat)), moduleName(std::move(n)), u(std::move(x)) {} @@ -2995,7 +2956,7 @@ struct InterfaceBody { // R1506 procedure-stmt -> [MODULE] PROCEDURE [::] specific-procedure-list struct ProcedureStmt { - enum class Kind { ModuleProcedure, Procedure }; + DEFINE_NESTED_ENUM_CLASS(Kind, ModuleProcedure, Procedure); TUPLE_CLASS_BOILERPLATE(ProcedureStmt); std::tuple> t; }; @@ -3184,7 +3145,6 @@ struct AssignedGotoStmt { WRAPPER_CLASS(PauseStmt, std::optional); // Formatting of template types -// TODO move elsewhere? template std::ostream &operator<<(std::ostream &o, const Statement &x) { return o << "(Statement " << x.label << ' ' @@ -3222,6 +3182,20 @@ std::ostream &operator<<(std::ostream &o, const LoopBounds &x) { return o << "(LoopBounds " << x.name << ' ' << x.lower << ' ' << x.upper << ' ' << x.step << ')'; } + +// Formatting enumerations defined via DEFINE_NESTED_ENUM_CLASS +#define NESTED_ENUM_TO_STRING(ENUMTYPE) \ + static std::string ToString(ENUMTYPE x) { \ + std::string str{ENUMTYPE##AsString}; \ + size_t start{0}; \ + for (int j{static_cast(x)}; j-- > 0;) { \ + start = str.find(',', start) + 1; \ + } \ + while (str[start] == ' ') { \ + ++start; \ + } \ + return str.substr(start, str.find(',', start)); \ + } } // namespace parser } // namespace Fortran #endif // FORTRAN_PARSER_PARSE_TREE_H_ diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc index f7461d1a54f9..91db2b694ea7 100644 --- a/flang/lib/parser/preprocessor.cc +++ b/flang/lib/parser/preprocessor.cc @@ -68,8 +68,39 @@ TokenSequence Definition::Tokenize(const std::vector &argNames, return result; } +static size_t AfterLastNonBlank(const TokenSequence &tokens) { + for (size_t j{tokens.size()}; j > 0; --j) { + if (!tokens[j - 1].IsBlank()) { + return j; + } + } + return 0; +} + +static TokenSequence Stringify( + const TokenSequence &tokens, AllSources *allSources) { + TokenSequence result; + Provenance quoteProvenance{allSources->CompilerInsertionProvenance('"')}; + result.PutNextTokenChar('"', quoteProvenance); + for (size_t j{0}; j < tokens.size(); ++j) { + const CharPointerWithLength &token{tokens[j]}; + size_t bytes{token.size()}; + for (size_t k{0}; k < bytes; ++k) { + char ch{token[k]}; + Provenance from{tokens.GetTokenProvenance(j, k)}; + if (ch == '"' || ch == '\\') { + result.PutNextTokenChar(ch, from); + } + result.PutNextTokenChar(ch, from); + } + } + result.PutNextTokenChar('"', quoteProvenance); + result.CloseToken(); + return result; +} + TokenSequence Definition::Apply( - const std::vector &args, const AllSources &allSources) { + const std::vector &args, AllSources *allSources) { TokenSequence result; bool pasting{false}; bool skipping{false}; @@ -93,35 +124,16 @@ TokenSequence Definition::Apply( if (index >= args.size()) { continue; } - size_t afterLastNonBlank{result.size()}; - for (; afterLastNonBlank > 0; --afterLastNonBlank) { - if (!result[afterLastNonBlank - 1].IsBlank()) { - break; - } - } - size_t argTokens{args[index].size()}; + size_t afterLastNonBlank{AfterLastNonBlank(result)}; if (afterLastNonBlank > 0 && result[afterLastNonBlank - 1].ToString() == "#") { + // stringifying while (result.size() >= afterLastNonBlank) { result.pop_back(); } - Provenance quoteProvenance{allSources.CompilerInsertionProvenance('"')}; - result.PutNextTokenChar('"', quoteProvenance); - for (size_t k{0}; k < argTokens; ++k) { - const CharPointerWithLength &arg{args[index][k]}; - size_t argBytes{args[index][k].size()}; - for (size_t n{0}; n < argBytes; ++n) { - char ch{arg[n]}; - Provenance from{args[index].GetTokenProvenance(k, n)}; - if (ch == '"' || ch == '\\') { - result.PutNextTokenChar(ch, from); - } - result.PutNextTokenChar(ch, from); - } - } - result.PutNextTokenChar('"', quoteProvenance); - result.CloseToken(); + result.Put(Stringify(args[index], allSources)); } else { + size_t argTokens{args[index].size()}; for (size_t k{0}; k < argTokens; ++k) { if (!pasting || !args[index][k].IsBlank()) { result.Put(args[index], k); @@ -143,9 +155,10 @@ TokenSequence Definition::Apply( // Delete whitespace immediately following ## in the body. } else if (bytes == 11 && isVariadic_ && token.ToString() == "__VA_ARGS__") { + Provenance commaProvenance{allSources->CompilerInsertionProvenance(',')}; for (size_t k{argumentCount_}; k < args.size(); ++k) { if (k > argumentCount_) { - result.Put(","s, allSources.CompilerInsertionProvenance(',')); + result.Put(","s, commaProvenance); } result.Put(args[k]); } @@ -205,7 +218,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input, } } if (j == tokens) { - return false; // nothing appeared that could be replaced + return false; // contains nothing that would be replaced } result->Put(input, 0, j); for (; j < tokens; ++j) { @@ -300,7 +313,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input, } def.set_isDisabled(true); TokenSequence replaced{ - ReplaceMacros(def.Apply(args, *allSources_), prescanner)}; + ReplaceMacros(def.Apply(args, allSources_), prescanner)}; def.set_isDisabled(false); if (!replaced.empty()) { ProvenanceRange from{def.replacement().GetProvenanceRange()}; diff --git a/flang/lib/parser/preprocessor.h b/flang/lib/parser/preprocessor.h index 8f5ecf6dc9bf..e84ac8e9fa36 100644 --- a/flang/lib/parser/preprocessor.h +++ b/flang/lib/parser/preprocessor.h @@ -37,8 +37,7 @@ public: bool set_isDisabled(bool disable); - TokenSequence Apply( - const std::vector &args, const AllSources &); + TokenSequence Apply(const std::vector &args, AllSources *); private: static TokenSequence Tokenize(const std::vector &argNames, diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc index 76c6488fef54..fcd52a6bca66 100644 --- a/flang/lib/parser/prescan.cc +++ b/flang/lib/parser/prescan.cc @@ -161,8 +161,11 @@ void Prescanner::NextChar() { } } while (*at_ == '\n' || *at_ == '&') { - if ((inFixedForm_ && !FixedFormContinuation()) || - (!inFixedForm_ && !FreeFormContinuation())) { + if (inFixedForm_) { + if (!FixedFormContinuation()) { + return; + } + } else if (!FreeFormContinuation()) { return; } } @@ -220,9 +223,7 @@ bool Prescanner::NextToken(TokenSequence *tokens) { EmitCharAndAdvance(tokens, 'h'); inCharLiteral_ = true; while (n-- > 0) { - if (PadOutCharacterLiteral()) { - tokens->PutNextTokenChar(' ', spaceProvenance_); - } else { + if (!PadOutCharacterLiteral(tokens)) { if (*at_ == '\n') { break; // TODO error } @@ -306,20 +307,42 @@ bool Prescanner::ExponentAndKind(TokenSequence *tokens) { return true; } +void Prescanner::EmitQuotedCharacter(TokenSequence *tokens, char ch) { + switch (ch) { + case '\a': EmitEscapedChar(tokens, 'a'); break; + case '\b': EmitEscapedChar(tokens, 'b'); break; + case '\f': EmitEscapedChar(tokens, 'f'); break; + case '\r': EmitEscapedChar(tokens, 'r'); break; + case '\t': EmitEscapedChar(tokens, 't'); break; + case '\v': EmitEscapedChar(tokens, 'v'); break; + case '\\': + if (!enableBackslashEscapesInCharLiterals_) { + EmitInsertedChar(tokens, '\\'); + } + EmitChar(tokens, '\\'); + break; + default: + if (ch < ' ') { + // emit an octal escape sequence + EmitInsertedChar(tokens, '\\'); + EmitInsertedChar(tokens, '0' + ((ch >> 6) & 3)); + EmitInsertedChar(tokens, '0' + ((ch >> 3) & 7)); + EmitInsertedChar(tokens, '0' + (ch & 7)); + } else { + EmitChar(tokens, ch); + } + } +} + void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) { char quote{*at_}; inCharLiteral_ = true; do { - EmitCharAndAdvance(tokens, *at_); - while (PadOutCharacterLiteral()) { - tokens->PutNextTokenChar(' ', spaceProvenance_); + EmitQuotedCharacter(tokens, *at_); + NextChar(); + while (PadOutCharacterLiteral(tokens)) { } - if (*at_ == '\\' && enableBackslashEscapesInCharLiterals_) { - EmitCharAndAdvance(tokens, '\\'); - while (PadOutCharacterLiteral()) { - tokens->PutNextTokenChar(' ', spaceProvenance_); - } - } else if (*at_ == quote) { + if (*at_ == quote) { // A doubled quote mark becomes a single instance of the quote character // in the literal later. EmitCharAndAdvance(tokens, quote); @@ -334,9 +357,10 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) { inCharLiteral_ = false; } -bool Prescanner::PadOutCharacterLiteral() { +bool Prescanner::PadOutCharacterLiteral(TokenSequence *tokens) { if (inFixedForm_ && !tabInCurrentLine_ && *at_ == '\n' && column_ < fixedFormColumnLimit_) { + tokens->PutNextTokenChar(' ', spaceProvenance_); ++column_; return true; } @@ -502,7 +526,7 @@ bool Prescanner::CommentLinesAndPreprocessorDirectives() { const char *Prescanner::FixedFormContinuationLine() { const char *p{lineStart_}; - if (p >= limit_) { + if (p >= limit_ || !inFixedForm_) { return nullptr; } tabInCurrentLine_ = false; @@ -539,6 +563,9 @@ bool Prescanner::FixedFormContinuation() { } bool Prescanner::FreeFormContinuation() { + if (inFixedForm_) { + return false; + } while (*at_ == ' ' || *at_ == '\t') { ++at_; } diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h index 50198f046913..8782f9d237e2 100644 --- a/flang/lib/parser/prescan.h +++ b/flang/lib/parser/prescan.h @@ -74,6 +74,17 @@ private: tokens->PutNextTokenChar(ch, GetCurrentProvenance()); } + void EmitInsertedChar(TokenSequence *tokens, char ch) { + Provenance provenance{ + cooked_->allSources()->CompilerInsertionProvenance(ch)}; + tokens->PutNextTokenChar(ch, provenance); + } + + void EmitEscapedChar(TokenSequence *tokens, char ch) { + EmitInsertedChar(tokens, '\\'); + EmitChar(tokens, ch); + } + char EmitCharAndAdvance(TokenSequence *tokens, char ch) { EmitChar(tokens, ch); NextChar(); @@ -86,8 +97,9 @@ private: void SkipSpaces(); bool NextToken(TokenSequence *); bool ExponentAndKind(TokenSequence *); + void EmitQuotedCharacter(TokenSequence *, char); void QuotedCharacterLiteral(TokenSequence *); - bool PadOutCharacterLiteral(); + bool PadOutCharacterLiteral(TokenSequence *); bool CommentLines(); bool CommentLinesAndPreprocessorDirectives(); bool IsFixedFormCommentLine(const char *); @@ -123,6 +135,8 @@ private: int delimiterNesting_{0}; Provenance spaceProvenance_{ cooked_->allSources()->CompilerInsertionProvenance(' ')}; + Provenance backslashProvenance_{ + cooked_->allSources()->CompilerInsertionProvenance('\\')}; }; } // namespace parser } // namespace Fortran diff --git a/flang/lib/parser/provenance.cc b/flang/lib/parser/provenance.cc index 7584116aec77..bec938cc9a9c 100644 --- a/flang/lib/parser/provenance.cc +++ b/flang/lib/parser/provenance.cc @@ -66,11 +66,6 @@ AllSources::AllSources() : range_{1, 1} { // so that provenance offset 0 remains reserved as an uninitialized // value. origin_.emplace_back(range_, std::string{'?'}); - - for (char ch : " ,\"01\n"s) { - compilerInsertionProvenance_[ch] = - AddCompilerInsertion(std::string{ch}).LocalOffsetToProvenance(0); - } } AllSources::~AllSources() {} @@ -231,8 +226,15 @@ int AllSources::GetLineNumber(Provenance at) const { return source ? source->FindOffsetLineAndColumn(offset).first : 0; } -Provenance AllSources::CompilerInsertionProvenance(char ch) const { - return compilerInsertionProvenance_.find(ch)->second; +Provenance AllSources::CompilerInsertionProvenance(char ch) { + auto iter = compilerInsertionProvenance_.find(ch); + if (iter != compilerInsertionProvenance_.end()) { + return iter->second; + } + ProvenanceRange newCharRange{AddCompilerInsertion(std::string{ch})}; + Provenance newCharProvenance{newCharRange.LocalOffsetToProvenance(0)}; + compilerInsertionProvenance_.insert(std::make_pair(ch, newCharProvenance)); + return newCharProvenance; } AllSources::Origin::Origin(ProvenanceRange r, const SourceFile &source) diff --git a/flang/lib/parser/provenance.h b/flang/lib/parser/provenance.h index 885a4428c7f4..df6b05e13816 100644 --- a/flang/lib/parser/provenance.h +++ b/flang/lib/parser/provenance.h @@ -176,7 +176,7 @@ public: ProvenanceRange GetContiguousRangeAround(ProvenanceRange) const; std::string GetPath(Provenance) const; // __FILE__ int GetLineNumber(Provenance) const; // __LINE__ - Provenance CompilerInsertionProvenance(char ch) const; + Provenance CompilerInsertionProvenance(char ch); void Dump(std::ostream &) const; private: diff --git a/flang/lib/parser/token-parsers.h b/flang/lib/parser/token-parsers.h index 9e7135fe67df..d652302d2dc2 100644 --- a/flang/lib/parser/token-parsers.h +++ b/flang/lib/parser/token-parsers.h @@ -199,7 +199,7 @@ struct CharLiteralChar { state->PutMessage(at, "unclosed character constant"_en_US); return {}; } - if (ch != '\\' || !state->enableBackslashEscapesInCharLiterals()) { + if (ch != '\\') { return {Result::Bare(ch)}; } if (!(och = nextChar.Parse(state)).has_value()) { diff --git a/flang/lib/parser/unparse.cc b/flang/lib/parser/unparse.cc index 6a5c48e9a4f0..a0788fe30ae3 100644 --- a/flang/lib/parser/unparse.cc +++ b/flang/lib/parser/unparse.cc @@ -165,11 +165,9 @@ public: Put("END MODULE"); return true; } - bool Pre(const MainProgram &x) { - if (std::get>>(x.t)) { - Put("PROGRAM "); - Indent(); - } + bool Pre(const ProgramStmt &x) { + Put("PROGRAM "); + Indent(); return true; } bool Pre(const EndProgramStmt &x) { diff --git a/flang/tools/f18/f18.cc b/flang/tools/f18/f18.cc index 75c5d9253964..89895b240f40 100644 --- a/flang/tools/f18/f18.cc +++ b/flang/tools/f18/f18.cc @@ -147,7 +147,6 @@ int main(int argc, char *const argv[]) { Fortran::parser::ParseState state{cooked}; Fortran::parser::UserState ustate; state.set_inFixedForm(fixedForm) - .set_enableBackslashEscapesInCharLiterals(backslashEscapes) .set_strictConformance(standard) .set_userState(&ustate); diff --git a/flang/tools/f18/test-type.cc b/flang/tools/f18/test-type.cc index 029fc333d9f0..fb6b63b7f356 100644 --- a/flang/tools/f18/test-type.cc +++ b/flang/tools/f18/test-type.cc @@ -202,7 +202,7 @@ static void visitDerivedTypeDef(const DerivedTypeDef &dtd) { std::visit( visitors{ [&](const TypeAttrSpec::Extends &extends) { - builder.extends(extends.name); + builder.extends(extends.v); }, [&](const TypeAttrSpec::BindC &) { builder.attr(semantics::Attr::BIND_C);