From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by sourceware.org (Postfix) with ESMTPS id C06B13852771; Wed, 24 Aug 2022 12:00:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C06B13852771 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wm1-x335.google.com with SMTP id d5so8649494wms.5; Wed, 24 Aug 2022 05:00:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=U5xIr0TO0UHY6rQGTc48j7AbYaTgYBe8N36n/9OXap8=; b=Cd3U2mD/8YJ+QlO1Bl2kPRYQ2YEN2S+m5maiGzbW57/R/gE7dg+fs6vUueTzD0kxD1 bDfh7rESySB8H5BQuWZkAs/p/FTcRPPaE/0xD0CeBH50CxyG9CiTokJFrtpJibIX7Axr uBoMt93ibHWB5+Jq4FuK7ku0fwLZotVnOFcBn07RQMtRyjyzXRoY/8wXGzcbEdRV8G6u 6sDylB3vRObT5grukikBBI93S7mnb5pOVfu/JPydA5FDzD8HlEcovUxtlHQC0buNAPdR /uQfgPMqHGfDHN4QfxOCoyyZPq/og3RSOzPBNnUin+mAPkmsMdjGYDFIGXZ8rMDPqfq/ eqMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=U5xIr0TO0UHY6rQGTc48j7AbYaTgYBe8N36n/9OXap8=; b=KEudvGSPX4KUS4zdMnu6Q0tKzMEIvLspkM8cFya0qxhShaSBEoUHhmqPLOKWxx3xD8 1tcb1UJ7Bhb3RvlGUTYyKYxPLQ/nrwbDy280tpLXnBh9e9Gn+RkKk/Pkb6jgRpQomok6 HoDtVwU2dbHQ1eMxAE8laFnY/J1P7xb/xLfTizHrgLtWCsC78jkUMvU7OeO5eYM5zUBZ tVa67tRusucYA/GLaLKG1ujP6T9W3ZQt0pV0gxFP94x8GPNZthq7xnUvoMzKg5BdPI+s /5/bG1AgsDMDPtZVwr2WoeKArSTO5lwKGEZljDuwLHh6lImawOfEIhJpmOdU4JBaB1FQ K5Sg== X-Gm-Message-State: ACgBeo1Z5xnlYtR4mQeYPiolT3Kz6ibrkOeMJQCnyjvZYSV+cSOM121n bOQbTejwjKy8upGumboW4oDUsda/XkA= X-Google-Smtp-Source: AA6agR5hQsROKDAkA/2YwjVpXmHK94+l+5WaggZ1SB10Zq5G2M50i+tqIDfTOpL4uzvdB0od811Cpg== X-Received: by 2002:a05:600c:410d:b0:3a6:1db8:b419 with SMTP id j13-20020a05600c410d00b003a61db8b419mr5020056wmi.119.1661342437215; Wed, 24 Aug 2022 05:00:37 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.00.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:00:36 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, The Other , Philip Herron Subject: [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures Date: Wed, 24 Aug 2022 12:59:27 +0100 Message-Id: <20220824115956.737931-9-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> Reply-To: philip.herron@embecosm.com MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: The Other This is a full C++11 class hierarchy representing the Rust AST. We do not allow dynamic_cast and so the main mechanism to work with the AST is by using the visitor interface. Slowly we are adding TREE_CODE style node types to the AST which will allow for more ways to work with the AST but for now this is it. See: https://doc.rust-lang.org/reference/items.html Co-authored-by: Philip Herron Co-authored-by: Arthur Cohen . + +#include "rust-ast-dump.h" + +namespace Rust { +namespace AST { + +Indent::Indent () : tabs (0) {} + +std::ostream & +operator<< (std::ostream &stream, const Indent &indent) +{ + return stream << std::string (indent.tabs, '\t'); +} + +void +Indent::increment () +{ + tabs++; +} + +void +Indent::decrement () +{ + rust_assert (tabs != 0); + tabs--; +} + +Dump::Dump (std::ostream &stream) : stream (stream), indentation (Indent ()) {} + +void +Dump::go (AST::Crate &crate) +{ + for (auto &item : crate.items) + { + stream << indentation; + item->accept_vis (*this); + stream << "\n"; + } +} + +void +Dump::go (AST::Item &item) +{ + item.accept_vis (*this); +} + +void +Dump::format_function_param (FunctionParam ¶m) +{ + param.get_pattern ()->accept_vis (*this); + stream << ": "; + param.get_type ()->accept_vis (*this); +} + +void +Dump::emit_attrib (const Attribute &attrib) +{ + stream << "#"; + stream << "["; + + for (size_t i = 0; i < attrib.get_path ().get_segments ().size (); i++) + { + const auto &seg = attrib.get_path ().get_segments ().at (i); + bool has_next = (i + 1) < attrib.get_path ().get_segments ().size (); + + stream << seg.get_segment_name (); + if (has_next) + stream << "::"; + } + + if (attrib.has_attr_input ()) + { + stream << " = "; + + bool is_literal = attrib.get_attr_input ().get_attr_input_type () + == AST::AttrInput::AttrInputType::LITERAL; + if (is_literal) + { + auto &literal + = static_cast (attrib.get_attr_input ()); + const auto &value = literal.get_literal ().as_string (); + + stream << "\"" << value << "\""; + } + else + { + stream << "FIXME"; + } + } + + stream << "]"; +} + +void +Dump::visit (Token &tok) +{} + +void +Dump::visit (DelimTokenTree &delim_tok_tree) +{} + +void +Dump::visit (AttrInputMetaItemContainer &input) +{} + +void +Dump::visit (IdentifierExpr &ident_expr) +{ + stream << ident_expr.get_ident (); +} + +void +Dump::visit (Lifetime &lifetime) +{} + +void +Dump::visit (LifetimeParam &lifetime_param) +{} + +void +Dump::visit (ConstGenericParam &lifetime_param) +{} + +// rust-path.h +void +Dump::visit (PathInExpression &path) +{} + +void +Dump::visit (TypePathSegment &segment) +{} + +void +Dump::visit (TypePathSegmentGeneric &segment) +{} + +void +Dump::visit (TypePathSegmentFunction &segment) +{} + +void +Dump::visit (TypePath &path) +{ + stream << path.as_string (); +} + +void +Dump::visit (QualifiedPathInExpression &path) +{} + +void +Dump::visit (QualifiedPathInType &path) +{} + +// rust-expr.h +void +Dump::visit (LiteralExpr &expr) +{ + stream << expr.as_string (); +} + +void +Dump::visit (AttrInputLiteral &attr_input) +{} + +void +Dump::visit (MetaItemLitExpr &meta_item) +{} + +void +Dump::visit (MetaItemPathLit &meta_item) +{} + +void +Dump::visit (BorrowExpr &expr) +{} + +void +Dump::visit (DereferenceExpr &expr) +{} + +void +Dump::visit (ErrorPropagationExpr &expr) +{} + +void +Dump::visit (NegationExpr &expr) +{} + +void +Dump::visit (ArithmeticOrLogicalExpr &expr) +{ + expr.get_left_expr ()->accept_vis (*this); + stream << " "; + + switch (expr.get_expr_type ()) + { + case ArithmeticOrLogicalOperator::ADD: + stream << "+"; + break; + + case ArithmeticOrLogicalOperator::SUBTRACT: + stream << "-"; + break; + + case ArithmeticOrLogicalOperator::MULTIPLY: + stream << "*"; + break; + + case ArithmeticOrLogicalOperator::DIVIDE: + stream << "/"; + break; + + case ArithmeticOrLogicalOperator::MODULUS: + stream << "%"; + break; + + case ArithmeticOrLogicalOperator::BITWISE_AND: + stream << "&"; + break; + + case ArithmeticOrLogicalOperator::BITWISE_OR: + stream << "|"; + break; + + case ArithmeticOrLogicalOperator::BITWISE_XOR: + stream << "^"; + break; + + case ArithmeticOrLogicalOperator::LEFT_SHIFT: + stream << "<<"; + break; + + case ArithmeticOrLogicalOperator::RIGHT_SHIFT: + stream << ">>"; + break; + } + + stream << " "; + expr.get_right_expr ()->accept_vis (*this); +} + +void +Dump::visit (ComparisonExpr &expr) +{} + +void +Dump::visit (LazyBooleanExpr &expr) +{} + +void +Dump::visit (TypeCastExpr &expr) +{} + +void +Dump::visit (AssignmentExpr &expr) +{} + +void +Dump::visit (CompoundAssignmentExpr &expr) +{} + +void +Dump::visit (GroupedExpr &expr) +{} + +void +Dump::visit (ArrayElemsValues &elems) +{} + +void +Dump::visit (ArrayElemsCopied &elems) +{} + +void +Dump::visit (ArrayExpr &expr) +{} + +void +Dump::visit (ArrayIndexExpr &expr) +{} + +void +Dump::visit (TupleExpr &expr) +{} + +void +Dump::visit (TupleIndexExpr &expr) +{} + +void +Dump::visit (StructExprStruct &expr) +{} + +void +Dump::visit (StructExprFieldIdentifier &field) +{} + +void +Dump::visit (StructExprFieldIdentifierValue &field) +{} + +void +Dump::visit (StructExprFieldIndexValue &field) +{} + +void +Dump::visit (StructExprStructFields &expr) +{} + +void +Dump::visit (StructExprStructBase &expr) +{} + +void +Dump::visit (CallExpr &expr) +{} + +void +Dump::visit (MethodCallExpr &expr) +{} + +void +Dump::visit (FieldAccessExpr &expr) +{} + +void +Dump::visit (ClosureExprInner &expr) +{} + +void +Dump::visit (BlockExpr &expr) +{ + stream << "{\n"; + indentation.increment (); + + for (auto &stmt : expr.get_statements ()) + { + stream << indentation; + stmt->accept_vis (*this); + stream << ";\n"; + } + + if (expr.has_tail_expr ()) + { + stream << indentation; + expr.get_tail_expr ()->accept_vis (*this); + } + + indentation.decrement (); + stream << "\n" << indentation << "}\n"; +} + +void +Dump::visit (ClosureExprInnerTyped &expr) +{} + +void +Dump::visit (ContinueExpr &expr) +{} + +void +Dump::visit (BreakExpr &expr) +{} + +void +Dump::visit (RangeFromToExpr &expr) +{} + +void +Dump::visit (RangeFromExpr &expr) +{} + +void +Dump::visit (RangeToExpr &expr) +{} + +void +Dump::visit (RangeFullExpr &expr) +{} + +void +Dump::visit (RangeFromToInclExpr &expr) +{} + +void +Dump::visit (RangeToInclExpr &expr) +{} + +void +Dump::visit (ReturnExpr &expr) +{} + +void +Dump::visit (UnsafeBlockExpr &expr) +{} + +void +Dump::visit (LoopExpr &expr) +{} + +void +Dump::visit (WhileLoopExpr &expr) +{} + +void +Dump::visit (WhileLetLoopExpr &expr) +{} + +void +Dump::visit (ForLoopExpr &expr) +{} + +void +Dump::visit (IfExpr &expr) +{} + +void +Dump::visit (IfExprConseqElse &expr) +{} + +void +Dump::visit (IfExprConseqIf &expr) +{} + +void +Dump::visit (IfExprConseqIfLet &expr) +{} + +void +Dump::visit (IfLetExpr &expr) +{} + +void +Dump::visit (IfLetExprConseqElse &expr) +{} + +void +Dump::visit (IfLetExprConseqIf &expr) +{} + +void +Dump::visit (IfLetExprConseqIfLet &expr) +{} + +void +Dump::visit (MatchExpr &expr) +{} + +void +Dump::visit (AwaitExpr &expr) +{} + +void +Dump::visit (AsyncBlockExpr &expr) +{} + +// rust-item.h +void +Dump::visit (TypeParam ¶m) +{ + stream << param.get_type_representation (); + if (param.has_type ()) + { + stream << " = "; + param.get_type ()->accept_vis (*this); + } +} + +void +Dump::visit (LifetimeWhereClauseItem &item) +{} + +void +Dump::visit (TypeBoundWhereClauseItem &item) +{} + +void +Dump::visit (Method &method) +{ + stream << indentation << "fn " << method.get_method_name () << '('; + + auto &self = method.get_self_param (); + stream << self.as_string (); + + auto ¶ms = method.get_function_params (); + for (auto ¶m : params) + { + stream << ", "; + format_function_param (param); + } + + stream << ") "; + + if (method.has_return_type ()) + { + stream << "-> "; + method.get_return_type ()->accept_vis (*this); + stream << " "; + } + + auto &block = method.get_definition (); + if (!block) + stream << ';'; + else + block->accept_vis (*this); + + stream << '\n'; +} + +void +Dump::visit (Module &module) +{} + +void +Dump::visit (ExternCrate &crate) +{} + +void +Dump::visit (UseTreeGlob &use_tree) +{} + +void +Dump::visit (UseTreeList &use_tree) +{} + +void +Dump::visit (UseTreeRebind &use_tree) +{} + +void +Dump::visit (UseDeclaration &use_decl) +{} + +void +Dump::visit (Function &function) +{ + stream << "fn " << function.get_function_name (); + + if (function.has_generics ()) + { + stream << "<"; + for (size_t i = 0; i < function.get_generic_params ().size (); i++) + { + auto ¶m = function.get_generic_params ().at (i); + param->accept_vis (*this); + + bool has_next = (i + 1) < function.get_generic_params ().size (); + if (has_next) + stream << ", "; + } + stream << ">"; + } + + stream << '('; + auto ¶ms = function.get_function_params (); + if (params.size () >= 1) + { + format_function_param (params[0]); + for (size_t i = 1; i < params.size (); i++) + { + stream << ", "; + format_function_param (params[i]); + } + } + + stream << ") "; + + if (function.has_return_type ()) + { + stream << "-> "; + function.get_return_type ()->accept_vis (*this); + stream << " "; + } + + auto &block = function.get_definition (); + if (!block) + stream << ';'; + else + block->accept_vis (*this); + + stream << '\n'; +} + +void +Dump::visit (TypeAlias &type_alias) +{} + +void +Dump::visit (StructStruct &struct_item) +{} + +void +Dump::visit (TupleStruct &tuple_struct) +{} + +void +Dump::visit (EnumItem &item) +{} + +void +Dump::visit (EnumItemTuple &item) +{} + +void +Dump::visit (EnumItemStruct &item) +{} + +void +Dump::visit (EnumItemDiscriminant &item) +{} + +void +Dump::visit (Enum &enum_item) +{} + +void +Dump::visit (Union &union_item) +{} + +void +Dump::visit (ConstantItem &const_item) +{} + +void +Dump::visit (StaticItem &static_item) +{} + +void +Dump::format_function_common (std::unique_ptr &return_type, + std::unique_ptr &block) +{ + if (return_type) + { + stream << "-> "; + return_type->accept_vis (*this); + } + + if (block) + { + if (return_type) + stream << ' '; + block->accept_vis (*this); + } + else + stream << ";\n"; +} + +void +Dump::visit (TraitItemFunc &item) +{ + auto func = item.get_trait_function_decl (); + stream << indentation << "fn " << func.get_identifier () << '('; + + auto ¶ms = func.get_function_params (); + for (auto ¶m : params) + { + stream << ", "; + format_function_param (param); + } + + stream << ") "; + + format_function_common (func.get_return_type (), item.get_definition ()); +} + +void +Dump::visit (TraitItemMethod &item) +{ + auto method = item.get_trait_method_decl (); + stream << indentation << "fn " << method.get_identifier () << '('; + + auto &self = method.get_self_param (); + stream << self.as_string (); + + auto ¶ms = method.get_function_params (); + for (auto ¶m : params) + { + stream << ", "; + format_function_param (param); + } + + stream << ") "; + + format_function_common (method.get_return_type (), item.get_definition ()); +} + +void +Dump::visit (TraitItemConst &item) +{ + stream << indentation << "const " << item.get_identifier () << ": "; + item.get_type ()->accept_vis (*this); + stream << ";\n"; +} + +void +Dump::visit (TraitItemType &item) +{ + stream << indentation << "type " << item.get_identifier () << ";\n"; +} + +void +Dump::visit (Trait &trait) +{ + for (const auto &attr : trait.get_outer_attrs ()) + { + emit_attrib (attr); + stream << "\n" << indentation; + } + + stream << "trait " << trait.get_identifier (); + + // Traits actually have an implicit Self thrown at the start so we must expect + // the number of generic params to be > 1 + if (trait.get_generic_params ().size () > 1) + { + stream << "<"; + for (size_t i = 1; i < trait.get_generic_params ().size (); i++) + { + auto ¶m = trait.get_generic_params ().at (i); + param->accept_vis (*this); + + bool has_next = (i + 1) < trait.get_generic_params ().size (); + if (has_next) + stream << ", "; + } + stream << ">"; + } + + stream << " {\n"; + + indentation.increment (); + + for (auto &item : trait.get_trait_items ()) + item->accept_vis (*this); + + indentation.decrement (); + stream << "\n}\n"; +} + +void +Dump::visit (InherentImpl &impl) +{ + stream << "impl "; + + // FIXME: Handle generics + + impl.get_type ()->accept_vis (*this); + + // FIXME: Handle where-clause + // FIXME: Handle inner attributes + + stream << " {\n"; + indentation.increment (); + + for (auto &item : impl.get_impl_items ()) + item->accept_vis (*this); + + indentation.decrement (); + stream << "\n}\n"; +} + +void +Dump::visit (TraitImpl &impl) +{ + stream << "impl "; + impl.get_trait_path ().accept_vis (*this); + stream << " for "; + impl.get_type ()->accept_vis (*this); + + stream << " {\n"; + indentation.increment (); + + for (auto &item : impl.get_impl_items ()) + item->accept_vis (*this); + + indentation.decrement (); + stream << "\n}\n"; +} + +void +Dump::visit (ExternalStaticItem &item) +{} + +void +Dump::visit (ExternalFunctionItem &function) +{ + stream << "fn " << function.get_identifier () << '('; + + for (size_t i = 0; i < function.get_function_params ().size (); i++) + { + auto ¶m = function.get_function_params ().at (i); + bool has_next = (i + 1) < function.get_function_params ().size (); + + stream << param.get_name () << ": "; + param.get_type ()->accept_vis (*this); + + if (has_next) + stream << ", "; + } + + stream << ')'; + if (function.has_return_type ()) + { + stream << "-> "; + function.get_return_type ()->accept_vis (*this); + } +} + +void +Dump::visit (ExternBlock &block) +{ + stream << "extern "; + + if (block.has_abi ()) + { + stream << "\""; + stream << block.get_abi (); + stream << "\" "; + } + + stream << "{\n"; + indentation.increment (); + + for (auto &item : block.get_extern_items ()) + { + stream << indentation; + item->accept_vis (*this); + stream << ";\n"; + } + + indentation.decrement (); + stream << "\n" << indentation << "}\n"; +} + +// rust-macro.h +void +Dump::visit (MacroMatchFragment &match) +{} + +void +Dump::visit (MacroMatchRepetition &match) +{} + +void +Dump::visit (MacroMatcher &matcher) +{} + +void +Dump::visit (MacroRulesDefinition &rules_def) +{} + +void +Dump::visit (MacroInvocation ¯o_invoc) +{} + +void +Dump::visit (MetaItemPath &meta_item) +{} + +void +Dump::visit (MetaItemSeq &meta_item) +{} + +void +Dump::visit (MetaWord &meta_item) +{} + +void +Dump::visit (MetaNameValueStr &meta_item) +{} + +void +Dump::visit (MetaListPaths &meta_item) +{} + +void +Dump::visit (MetaListNameValueStr &meta_item) +{} + +// rust-pattern.h +void +Dump::visit (LiteralPattern &pattern) +{} + +void +Dump::visit (IdentifierPattern &pattern) +{ + stream << pattern.get_ident (); +} + +void +Dump::visit (WildcardPattern &pattern) +{} + +// void Dump::visit(RangePatternBound& bound){} + +void +Dump::visit (RangePatternBoundLiteral &bound) +{} + +void +Dump::visit (RangePatternBoundPath &bound) +{} + +void +Dump::visit (RangePatternBoundQualPath &bound) +{} + +void +Dump::visit (RangePattern &pattern) +{} + +void +Dump::visit (ReferencePattern &pattern) +{} + +// void Dump::visit(StructPatternField& field){} + +void +Dump::visit (StructPatternFieldTuplePat &field) +{} + +void +Dump::visit (StructPatternFieldIdentPat &field) +{} + +void +Dump::visit (StructPatternFieldIdent &field) +{} + +void +Dump::visit (StructPattern &pattern) +{} + +// void Dump::visit(TupleStructItems& tuple_items){} + +void +Dump::visit (TupleStructItemsNoRange &tuple_items) +{} + +void +Dump::visit (TupleStructItemsRange &tuple_items) +{} + +void +Dump::visit (TupleStructPattern &pattern) +{} + +// void Dump::visit(TuplePatternItems& tuple_items){} + +void +Dump::visit (TuplePatternItemsMultiple &tuple_items) +{} + +void +Dump::visit (TuplePatternItemsRanged &tuple_items) +{} + +void +Dump::visit (TuplePattern &pattern) +{} + +void +Dump::visit (GroupedPattern &pattern) +{} + +void +Dump::visit (SlicePattern &pattern) +{} + +// rust-stmt.h +void +Dump::visit (EmptyStmt &stmt) +{} + +void +Dump::visit (LetStmt &stmt) +{ + stream << "let "; + auto &pattern = stmt.get_pattern (); + if (pattern) + pattern->accept_vis (*this); + + if (stmt.has_type ()) + { + stream << ": "; + stmt.get_type ()->accept_vis (*this); + } + + if (stmt.has_init_expr ()) + { + stream << " = "; + stmt.get_init_expr ()->accept_vis (*this); + } +} + +void +Dump::visit (ExprStmtWithoutBlock &stmt) +{} + +void +Dump::visit (ExprStmtWithBlock &stmt) +{} + +// rust-type.h +void +Dump::visit (TraitBound &bound) +{} + +void +Dump::visit (ImplTraitType &type) +{} + +void +Dump::visit (TraitObjectType &type) +{} + +void +Dump::visit (ParenthesisedType &type) +{} + +void +Dump::visit (ImplTraitTypeOneBound &type) +{} + +void +Dump::visit (TraitObjectTypeOneBound &type) +{} + +void +Dump::visit (TupleType &type) +{} + +void +Dump::visit (NeverType &type) +{} + +void +Dump::visit (RawPointerType &type) +{} + +void +Dump::visit (ReferenceType &type) +{ + type.get_type_referenced ()->accept_vis (*this); +} + +void +Dump::visit (ArrayType &type) +{ + type.get_elem_type ()->accept_vis (*this); +} + +void +Dump::visit (SliceType &type) +{ + type.get_elem_type ()->accept_vis (*this); +} + +void +Dump::visit (InferredType &type) +{ + stream << "_"; +} + +void +Dump::visit (BareFunctionType &type) +{} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/ast/rust-ast-dump.h b/gcc/rust/ast/rust-ast-dump.h new file mode 100644 index 00000000000..c3854e8287d --- /dev/null +++ b/gcc/rust/ast/rust-ast-dump.h @@ -0,0 +1,246 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-ast-visitor.h" +#include "rust-ast.h" +#include "rust-ast-full.h" + +#ifndef RUST_AST_DUMP_H +#define RUST_AST_DUMP_H + +namespace Rust { +namespace AST { + +// TODO: We might want to reuse this class somewhere else +class Indent +{ +public: + Indent (); + + friend std::ostream &operator<< (std::ostream &stream, const Indent &indent); + + void increment (); + void decrement (); + +private: + size_t tabs; +}; + +class Dump : public ASTVisitor +{ +public: + Dump (std::ostream &stream); + + /** + * Run the visitor on an entire crate and its items + */ + void go (AST::Crate &crate); + void go (AST::Item &item); + +private: + std::ostream &stream; + Indent indentation; + + // Format together common items of functions: Parameters, return type, block + void format_function_common (std::unique_ptr &return_type, + std::unique_ptr &block); + + /** + * Format a function's definition parameter + */ + void format_function_param (FunctionParam ¶m); + void emit_attrib (const Attribute &attrib); + + // rust-ast.h + void visit (Token &tok); + void visit (DelimTokenTree &delim_tok_tree); + void visit (AttrInputMetaItemContainer &input); + void visit (IdentifierExpr &ident_expr); + void visit (Lifetime &lifetime); + void visit (LifetimeParam &lifetime_param); + void visit (ConstGenericParam &const_param); + + // rust-path.h + void visit (PathInExpression &path); + void visit (TypePathSegment &segment); + void visit (TypePathSegmentGeneric &segment); + void visit (TypePathSegmentFunction &segment); + void visit (TypePath &path); + void visit (QualifiedPathInExpression &path); + void visit (QualifiedPathInType &path); + + // rust-expr.h + void visit (LiteralExpr &expr); + void visit (AttrInputLiteral &attr_input); + void visit (MetaItemLitExpr &meta_item); + void visit (MetaItemPathLit &meta_item); + void visit (BorrowExpr &expr); + void visit (DereferenceExpr &expr); + void visit (ErrorPropagationExpr &expr); + void visit (NegationExpr &expr); + void visit (ArithmeticOrLogicalExpr &expr); + void visit (ComparisonExpr &expr); + void visit (LazyBooleanExpr &expr); + void visit (TypeCastExpr &expr); + void visit (AssignmentExpr &expr); + void visit (CompoundAssignmentExpr &expr); + void visit (GroupedExpr &expr); + void visit (ArrayElemsValues &elems); + void visit (ArrayElemsCopied &elems); + void visit (ArrayExpr &expr); + void visit (ArrayIndexExpr &expr); + void visit (TupleExpr &expr); + void visit (TupleIndexExpr &expr); + void visit (StructExprStruct &expr); + void visit (StructExprFieldIdentifier &field); + void visit (StructExprFieldIdentifierValue &field); + void visit (StructExprFieldIndexValue &field); + void visit (StructExprStructFields &expr); + void visit (StructExprStructBase &expr); + void visit (CallExpr &expr); + void visit (MethodCallExpr &expr); + void visit (FieldAccessExpr &expr); + void visit (ClosureExprInner &expr); + void visit (BlockExpr &expr); + void visit (ClosureExprInnerTyped &expr); + void visit (ContinueExpr &expr); + void visit (BreakExpr &expr); + void visit (RangeFromToExpr &expr); + void visit (RangeFromExpr &expr); + void visit (RangeToExpr &expr); + void visit (RangeFullExpr &expr); + void visit (RangeFromToInclExpr &expr); + void visit (RangeToInclExpr &expr); + void visit (ReturnExpr &expr); + void visit (UnsafeBlockExpr &expr); + void visit (LoopExpr &expr); + void visit (WhileLoopExpr &expr); + void visit (WhileLetLoopExpr &expr); + void visit (ForLoopExpr &expr); + void visit (IfExpr &expr); + void visit (IfExprConseqElse &expr); + void visit (IfExprConseqIf &expr); + void visit (IfExprConseqIfLet &expr); + void visit (IfLetExpr &expr); + void visit (IfLetExprConseqElse &expr); + void visit (IfLetExprConseqIf &expr); + void visit (IfLetExprConseqIfLet &expr); + void visit (MatchExpr &expr); + void visit (AwaitExpr &expr); + void visit (AsyncBlockExpr &expr); + + // rust-item.h + void visit (TypeParam ¶m); + void visit (LifetimeWhereClauseItem &item); + void visit (TypeBoundWhereClauseItem &item); + void visit (Method &method); + void visit (Module &module); + void visit (ExternCrate &crate); + void visit (UseTreeGlob &use_tree); + void visit (UseTreeList &use_tree); + void visit (UseTreeRebind &use_tree); + void visit (UseDeclaration &use_decl); + void visit (Function &function); + void visit (TypeAlias &type_alias); + void visit (StructStruct &struct_item); + void visit (TupleStruct &tuple_struct); + void visit (EnumItem &item); + void visit (EnumItemTuple &item); + void visit (EnumItemStruct &item); + void visit (EnumItemDiscriminant &item); + void visit (Enum &enum_item); + void visit (Union &union_item); + void visit (ConstantItem &const_item); + void visit (StaticItem &static_item); + void visit (TraitItemFunc &item); + void visit (TraitItemMethod &item); + void visit (TraitItemConst &item); + void visit (TraitItemType &item); + void visit (Trait &trait); + void visit (InherentImpl &impl); + void visit (TraitImpl &impl); + void visit (ExternalStaticItem &item); + void visit (ExternalFunctionItem &item); + void visit (ExternBlock &block); + + // rust-macro.h + void visit (MacroMatchFragment &match); + void visit (MacroMatchRepetition &match); + void visit (MacroMatcher &matcher); + void visit (MacroRulesDefinition &rules_def); + void visit (MacroInvocation ¯o_invoc); + void visit (MetaItemPath &meta_item); + void visit (MetaItemSeq &meta_item); + void visit (MetaWord &meta_item); + void visit (MetaNameValueStr &meta_item); + void visit (MetaListPaths &meta_item); + void visit (MetaListNameValueStr &meta_item); + + // rust-pattern.h + void visit (LiteralPattern &pattern); + void visit (IdentifierPattern &pattern); + void visit (WildcardPattern &pattern); + // void visit(RangePatternBound& bound); + void visit (RangePatternBoundLiteral &bound); + void visit (RangePatternBoundPath &bound); + void visit (RangePatternBoundQualPath &bound); + void visit (RangePattern &pattern); + void visit (ReferencePattern &pattern); + // void visit(StructPatternField& field); + void visit (StructPatternFieldTuplePat &field); + void visit (StructPatternFieldIdentPat &field); + void visit (StructPatternFieldIdent &field); + void visit (StructPattern &pattern); + // void visit(TupleStructItems& tuple_items); + void visit (TupleStructItemsNoRange &tuple_items); + void visit (TupleStructItemsRange &tuple_items); + void visit (TupleStructPattern &pattern); + // void visit(TuplePatternItems& tuple_items); + void visit (TuplePatternItemsMultiple &tuple_items); + void visit (TuplePatternItemsRanged &tuple_items); + void visit (TuplePattern &pattern); + void visit (GroupedPattern &pattern); + void visit (SlicePattern &pattern); + + // rust-stmt.h + void visit (EmptyStmt &stmt); + void visit (LetStmt &stmt); + void visit (ExprStmtWithoutBlock &stmt); + void visit (ExprStmtWithBlock &stmt); + + // rust-type.h + void visit (TraitBound &bound); + void visit (ImplTraitType &type); + void visit (TraitObjectType &type); + void visit (ParenthesisedType &type); + void visit (ImplTraitTypeOneBound &type); + void visit (TraitObjectTypeOneBound &type); + void visit (TupleType &type); + void visit (NeverType &type); + void visit (RawPointerType &type); + void visit (ReferenceType &type); + void visit (ArrayType &type); + void visit (SliceType &type); + void visit (InferredType &type); + void visit (BareFunctionType &type); +}; + +} // namespace AST +} // namespace Rust + +#endif // !RUST_AST_DUMP_H diff --git a/gcc/rust/ast/rust-ast-full-decls.h b/gcc/rust/ast/rust-ast-full-decls.h new file mode 100644 index 00000000000..47f332193cc --- /dev/null +++ b/gcc/rust/ast/rust-ast-full-decls.h @@ -0,0 +1,273 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_FULL_DECLS_H +#define RUST_AST_FULL_DECLS_H + +// Forward declarations for all AST classes. Useful for not having to include +// all definitions. + +namespace Rust { +namespace AST { +// rust-ast.h +class AttrInput; +class TokenTree; +class MacroMatch; +class Token; +struct Literal; +class DelimTokenTree; +class PathSegment; +class SimplePathSegment; +class SimplePath; +struct Attribute; +class MetaItemInner; +class AttrInputMetaItemContainer; +class MetaItem; +class Stmt; +class Item; +class Expr; +class ExprWithoutBlock; +class IdentifierExpr; +class Pattern; +class Type; +class TypeNoBounds; +class TypeParamBound; +class Lifetime; +class GenericParam; +class LifetimeParam; +class ConstGenericParam; +class MacroItem; +class TraitItem; +class InherentImplItem; +class TraitImplItem; +struct Crate; +class PathExpr; + +// rust-path.h +class PathIdentSegment; +struct GenericArgsBinding; +struct GenericArgs; +class PathExprSegment; +class PathPattern; +class PathInExpression; +class TypePathSegment; +class TypePathSegmentGeneric; +struct TypePathFunction; +class TypePathSegmentFunction; +class TypePath; +struct QualifiedPathType; +class QualifiedPathInExpression; +class QualifiedPathInType; + +// rust-expr.h +class ExprWithBlock; +class LiteralExpr; +class AttrInputLiteral; +class MetaItemLitExpr; +class MetaItemPathLit; +class OperatorExpr; +class BorrowExpr; +class DereferenceExpr; +class ErrorPropagationExpr; +class NegationExpr; +class ArithmeticOrLogicalExpr; +class ComparisonExpr; +class LazyBooleanExpr; +class TypeCastExpr; +class AssignmentExpr; +class CompoundAssignmentExpr; +class GroupedExpr; +class ArrayElems; +class ArrayElemsValues; +class ArrayElemsCopied; +class ArrayExpr; +class ArrayIndexExpr; +class TupleExpr; +class TupleIndexExpr; +class StructExpr; +class StructExprStruct; +struct StructBase; +class StructExprField; +class StructExprFieldIdentifier; +class StructExprFieldWithVal; +class StructExprFieldIdentifierValue; +class StructExprFieldIndexValue; +class StructExprStructFields; +class StructExprStructBase; +class CallExpr; +class MethodCallExpr; +class FieldAccessExpr; +struct ClosureParam; +class ClosureExpr; +class ClosureExprInner; +class BlockExpr; +class ClosureExprInnerTyped; +class ContinueExpr; +class BreakExpr; +class RangeExpr; +class RangeFromToExpr; +class RangeFromExpr; +class RangeToExpr; +class RangeFullExpr; +class RangeFromToInclExpr; +class RangeToInclExpr; +class ReturnExpr; +class UnsafeBlockExpr; +class LoopLabel; +class BaseLoopExpr; +class LoopExpr; +class WhileLoopExpr; +class WhileLetLoopExpr; +class ForLoopExpr; +class IfExpr; +class IfExprConseqElse; +class IfExprConseqIf; +class IfLetExpr; +class IfExprConseqIfLet; +class IfLetExprConseqElse; +class IfLetExprConseqIf; +class IfLetExprConseqIfLet; +struct MatchArm; +// class MatchCase; +// class MatchCaseBlockExpr; +// class MatchCaseExpr; +struct MatchCase; +class MatchExpr; +class AwaitExpr; +class AsyncBlockExpr; + +// rust-stmt.h +class EmptyStmt; +class LetStmt; +class ExprStmt; +class ExprStmtWithoutBlock; +class ExprStmtWithBlock; + +// rust-item.h +class TypeParam; +class WhereClauseItem; +class LifetimeWhereClauseItem; +class TypeBoundWhereClauseItem; +struct WhereClause; +struct SelfParam; +struct FunctionQualifiers; +struct FunctionParam; +struct Visibility; +class Method; +class VisItem; +class Module; +class ExternCrate; +class UseTree; +class UseTreeGlob; +class UseTreeList; +class UseTreeRebind; +class UseDeclaration; +class Function; +class TypeAlias; +class Struct; +struct StructField; +class StructStruct; +struct TupleField; +class TupleStruct; +class EnumItem; +class EnumItemTuple; +class EnumItemStruct; +class EnumItemDiscriminant; +class Enum; +class Union; +class ConstantItem; +class StaticItem; +struct TraitFunctionDecl; +class TraitItemFunc; +struct TraitMethodDecl; +class TraitItemMethod; +class TraitItemConst; +class TraitItemType; +class Trait; +class Impl; +class InherentImpl; +class TraitImpl; +class ExternalItem; +class ExternalStaticItem; +struct NamedFunctionParam; +class ExternalFunctionItem; +class ExternBlock; + +// rust-macro.h +class MacroMatchFragment; +class MacroMatchRepetition; +class MacroMatcher; +struct MacroTranscriber; +struct MacroRule; +class MacroRulesDefinition; +class MacroInvocation; +class MetaItemPath; +class MetaItemSeq; +class MetaWord; +class MetaNameValueStr; +class MetaListPaths; +class MetaListNameValueStr; + +// rust-pattern.h +class LiteralPattern; +class IdentifierPattern; +class WildcardPattern; +class RangePatternBound; +class RangePatternBoundLiteral; +class RangePatternBoundPath; +class RangePatternBoundQualPath; +class RangePattern; +class ReferencePattern; +struct StructPatternEtc; +class StructPatternField; +class StructPatternFieldTuplePat; +class StructPatternFieldIdentPat; +class StructPatternFieldIdent; +struct StructPatternElements; +class StructPattern; +class TupleStructItems; +class TupleStructItemsNoRange; +class TupleStructItemsRange; +class TupleStructPattern; +class TuplePatternItems; +class TuplePatternItemsMultiple; +class TuplePatternItemsRanged; +class TuplePattern; +class GroupedPattern; +class SlicePattern; + +// rust-type.h +class TraitBound; +class ImplTraitType; +class TraitObjectType; +class ParenthesisedType; +class ImplTraitTypeOneBound; +class TraitObjectTypeOneBound; +class TupleType; +class NeverType; +class RawPointerType; +class ReferenceType; +class ArrayType; +class SliceType; +class InferredType; +struct MaybeNamedParam; +class BareFunctionType; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc new file mode 100644 index 00000000000..cac816d2545 --- /dev/null +++ b/gcc/rust/ast/rust-ast-full-test.cc @@ -0,0 +1,5814 @@ +/* General AST-related method implementations for Rust frontend. + Copyright (C) 2009-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +// FIXME: This does not work on Windows +#include +#include +#include + +#include "rust-ast-full.h" +#include "rust-diagnostics.h" +#include "rust-ast-visitor.h" +#include "rust-macro.h" +#include "rust-session-manager.h" +#include "rust-lex.h" +#include "rust-parse.h" +#include "operator.h" + +/* Compilation unit used for various AST-related functions that would make + * the headers too long if they were defined inline and don't receive any + * benefits from being defined inline because they are virtual. Also used + * for various other stuff. */ + +namespace Rust { +namespace AST { + +enum indent_mode +{ + enter, + out, + stay +}; + +std::string +indent_spaces (enum indent_mode mode) +{ + static int indent = 0; + std::string str = ""; + if (out == mode) + indent--; + for (int i = 0; i < indent; i++) + str += " "; + if (enter == mode) + indent++; + + return str; +} + +// Gets a string in a certain delim type. +std::string +get_string_in_delims (std::string str_input, DelimType delim_type) +{ + switch (delim_type) + { + case PARENS: + return "(" + str_input + ")"; + case SQUARE: + return "[" + str_input + "]"; + case CURLY: + return "{" + str_input + "}"; + default: + return "ERROR-MARK-STRING (delims)"; + } + gcc_unreachable (); +} + +enum AttrMode +{ + OUTER, + INNER +}; + +std::string +get_mode_dump_desc (AttrMode mode) +{ + switch (mode) + { + case OUTER: + return "outer attributes"; + case INNER: + return "inner attributes"; + default: + gcc_unreachable (); + return ""; + } +} + +// Adds lines below adding attributes +std::string +append_attributes (std::vector attrs, AttrMode mode) +{ + indent_spaces (enter); + + std::string str + = "\n" + indent_spaces (stay) + get_mode_dump_desc (mode) + ": "; + // str += "\n" + indent_spaces (stay) + "inner attributes: "; + if (attrs.empty ()) + { + str += "none"; + } + else + { + /* note that this does not print them with outer or "inner attribute" + * syntax - just prints the body */ + for (const auto &attr : attrs) + str += "\n" + indent_spaces (stay) + attr.as_string (); + } + + indent_spaces (out); + + return str; +} + +// Removes the beginning and end quotes of a quoted string. +std::string +unquote_string (std::string input) +{ + rust_assert (input.front () == '"'); + rust_assert (input.back () == '"'); + return input.substr (1, input.length () - 2); +} + +std::string +Crate::as_string () const +{ + rust_debug ("beginning crate recursive as-string"); + + std::string str ("Crate: "); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + // items + str += "\n items: "; + if (items.empty ()) + { + str += "none"; + } + else + { + for (const auto &item : items) + { + // DEBUG: null pointer check + if (item == nullptr) + { + rust_debug ("something really terrible has gone wrong - " + "null pointer item in crate."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + item->as_string (); + } + } + + return str + "\n"; +} + +std::string +Attribute::as_string () const +{ + std::string path_str = path.as_string (); + if (attr_input == nullptr) + return path_str; + else + return path_str + attr_input->as_string (); +} + +// Copy constructor must deep copy attr_input as unique pointer +Attribute::Attribute (Attribute const &other) + : path (other.path), locus (other.locus) +{ + // guard to protect from null pointer dereference + if (other.attr_input != nullptr) + attr_input = other.attr_input->clone_attr_input (); +} + +// overload assignment operator to use custom clone method +Attribute & +Attribute::operator= (Attribute const &other) +{ + path = other.path; + locus = other.locus; + // guard to protect from null pointer dereference + if (other.attr_input != nullptr) + attr_input = other.attr_input->clone_attr_input (); + else + attr_input = nullptr; + + return *this; +} + +std::string +DelimTokenTree::as_string () const +{ + std::string start_delim; + std::string end_delim; + switch (delim_type) + { + case PARENS: + start_delim = "("; + end_delim = ")"; + break; + case SQUARE: + start_delim = "["; + end_delim = "]"; + break; + case CURLY: + start_delim = "{"; + end_delim = "}"; + break; + default: + rust_debug ("Invalid delimiter type, " + "Should be PARENS, SQUARE, or CURLY."); + return "Invalid delimiter type"; + } + std::string str = start_delim; + if (!token_trees.empty ()) + { + for (const auto &tree : token_trees) + { + // DEBUG: null pointer check + if (tree == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "token tree in delim token tree."); + return "NULL_POINTER_MARK"; + } + + str += tree->as_string (); + } + } + str += end_delim; + + return str; +} + +std::string +Token::as_string () const +{ + if (tok_ref->has_str ()) + { + std::string str = tok_ref->get_str (); + + std::string quote = is_string_lit () ? "\"" : ""; + return quote + str + quote; + } + else + { + return tok_ref->get_token_description (); + } +} + +std::string +SimplePathSegment::as_string () const +{ + return segment_name; +} + +std::string +SimplePath::as_string () const +{ + std::string path; + if (has_opening_scope_resolution) + path = "::"; + + // crappy hack because doing proper for loop would be more code + bool first_time = true; + for (const auto &segment : segments) + { + if (first_time) + { + path += segment.as_string (); + first_time = false; + } + else + { + path += "::" + segment.as_string (); + } + + // DEBUG: remove later. Checks for path error. + if (segment.is_error ()) + { + rust_debug ("segment in path is error - this should've been filtered " + "out. first segment " + "was '%s'", + segments.at (0).as_string ().c_str ()); + } + } + + return path; +} + +std::string +Visibility::as_string () const +{ + switch (vis_type) + { + case PRIV: + return std::string (""); + case PUB: + return std::string ("pub"); + case PUB_CRATE: + return std::string ("pub(crate)"); + case PUB_SELF: + return std::string ("pub(self)"); + case PUB_SUPER: + return std::string ("pub(super)"); + case PUB_IN_PATH: + return std::string ("pub(in ") + in_path.as_string () + std::string (")"); + default: + gcc_unreachable (); + } +} + +// Creates a string that reflects the visibility stored. +std::string +VisItem::as_string () const +{ + // FIXME: can't do formatting on string to make identation occur. + std::string str; + + if (!outer_attrs.empty ()) + { + for (const auto &attr : outer_attrs) + str += attr.as_string () + "\n"; + } + + if (has_visibility ()) + str += visibility.as_string () + " "; + + return str; +} + +std::string +Module::as_string () const +{ + std::string str = VisItem::as_string () + "mod " + module_name; + + // Return early if we're dealing with an unloaded module as their body resides + // in a different file + if (kind == ModuleKind::UNLOADED) + return str + "\n no body (reference to external file)\n"; + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + // items + str += "\n items: "; + + // This can still happen if the module is loaded but empty, i.e. `mod foo {}` + if (items.empty ()) + { + str += "none"; + } + else + { + for (const auto &item : items) + { + // DEBUG: null pointer check + if (item == nullptr) + { + rust_debug ("something really terrible has gone wrong - " + "null pointer item in crate."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + item->as_string (); + } + } + + return str + "\n"; +} + +std::string +StaticItem::as_string () const +{ + std::string str = VisItem::as_string (); + + str += indent_spaces (stay) + "static"; + + if (has_mut) + str += " mut"; + + str += " " + name; + + // DEBUG: null pointer check + if (type == nullptr) + { + rust_debug ("something really terrible has gone wrong - null " + "pointer type in static item."); + return "NULL_POINTER_MARK"; + } + str += "\n" + indent_spaces (stay) + "Type: " + type->as_string (); + + // DEBUG: null pointer check + if (expr == nullptr) + { + rust_debug ("something really terrible has gone wrong - null " + "pointer expr in static item."); + return "NULL_POINTER_MARK"; + } + str += "\n" + indent_spaces (stay) + "Expression: " + expr->as_string (); + + return str + "\n"; +} + +std::string +ExternCrate::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "extern crate " + referenced_crate; + + if (has_as_clause ()) + str += " as " + as_clause_name; + + return str; +} + +std::string +TupleStruct::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "struct " + struct_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in enum."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + // tuple fields + str += "\n Tuple fields: "; + if (fields.empty ()) + { + str += "none"; + } + else + { + for (const auto &field : fields) + str += "\n " + field.as_string (); + } + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + return str; +} + +std::string +ConstantItem::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "const " + identifier; + + // DEBUG: null pointer check + if (type == nullptr) + { + rust_debug ("something really terrible has gone wrong - null " + "pointer type in const item."); + return "NULL_POINTER_MARK"; + } + str += "\n Type: " + type->as_string (); + + // DEBUG: null pointer check + if (const_expr == nullptr) + { + rust_debug ("something really terrible has gone wrong - null " + "pointer expr in const item."); + return "NULL_POINTER_MARK"; + } + str += "\n Expression: " + const_expr->as_string (); + + return str + "\n"; +} + +std::string +InherentImpl::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "impl "; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in inherent impl."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Type: " + trait_type->as_string (); + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + // inherent impl items + str += "\n Inherent impl items: "; + if (!has_impl_items ()) + { + str += "none"; + } + else + { + for (const auto &item : impl_items) + str += "\n " + item->as_string (); + } + + return str; +} + +std::string +Method::as_string () const +{ + std::string str ("Method: \n "); + + str += vis.as_string () + " " + qualifiers.as_string (); + + str += " fn " + method_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in method."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Self param: " + self_param.as_string (); + + str += "\n Function params: "; + if (function_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : function_params) + str += "\n " + param.as_string (); + } + + str += "\n Return type: "; + if (has_return_type ()) + str += return_type->as_string (); + else + str += "none (void)"; + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + str += "\n Block expr (body): \n "; + str += function_body->as_string (); + + return str; +} + +std::string +StructStruct::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "struct " + struct_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in enum."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + // struct fields + str += "\n Struct fields: "; + if (is_unit) + { + str += "none (unit)"; + } + else if (fields.empty ()) + { + str += "none (non-unit)"; + } + else + { + for (const auto &field : fields) + str += "\n " + field.as_string (); + } + + return str; +} + +std::string +UseDeclaration::as_string () const +{ + std::string str = VisItem::as_string (); + + // DEBUG: null pointer check + if (use_tree == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer use tree in " + "use declaration."); + return "NULL_POINTER_MARK"; + } + + str += "use " + use_tree->as_string (); + + return str; +} + +std::string +UseTreeGlob::as_string () const +{ + switch (glob_type) + { + case NO_PATH: + return "*"; + case GLOBAL: + return "::*"; + case PATH_PREFIXED: { + std::string path_str = path.as_string (); + return path_str + "::*"; + } + default: + // some kind of error + return "ERROR-PATH"; + } + gcc_unreachable (); +} + +std::string +UseTreeList::as_string () const +{ + std::string path_str; + switch (path_type) + { + case NO_PATH: + path_str = "{"; + break; + case GLOBAL: + path_str = "::{"; + break; + case PATH_PREFIXED: { + path_str = path.as_string () + "::{"; + break; + } + default: + // some kind of error + return "ERROR-PATH-LIST"; + } + + if (has_trees ()) + { + auto i = trees.begin (); + auto e = trees.end (); + + // DEBUG: null pointer check + if (*i == nullptr) + { + rust_debug ("something really terrible has gone wrong - null pointer " + "tree in use tree list."); + return "NULL_POINTER_MARK"; + } + + for (; i != e; i++) + { + path_str += (*i)->as_string (); + if (e != i + 1) + path_str += ", "; + } + } + else + { + path_str += "none"; + } + + return path_str + "}"; +} + +std::string +UseTreeRebind::as_string () const +{ + std::string path_str = path.as_string (); + + switch (bind_type) + { + case NONE: + // nothing to add, just path + break; + case IDENTIFIER: + path_str += " as " + identifier; + break; + case WILDCARD: + path_str += " as _"; + break; + default: + // error + return "ERROR-PATH-REBIND"; + } + + return path_str; +} + +std::string +Enum::as_string () const +{ + std::string str = VisItem::as_string (); + str += enum_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in enum."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + // items + str += "\n Items: "; + if (items.empty ()) + { + str += "none"; + } + else + { + for (const auto &item : items) + { + // DEBUG: null pointer check + if (item == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "enum item in enum."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + item->as_string (); + } + } + + return str; +} + +std::string +Trait::as_string () const +{ + std::string str = VisItem::as_string (); + + if (has_unsafe) + str += "unsafe "; + + str += "trait " + name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in trait."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Type param bounds: "; + if (!has_type_param_bounds ()) + { + str += "none"; + } + else + { + for (const auto &bound : type_param_bounds) + { + // DEBUG: null pointer check + if (bound == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "type param bound in trait."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + bound->as_string (); + } + } + + str += "\n Where clause: "; + if (!has_where_clause ()) + str += "none"; + else + str += where_clause.as_string (); + + str += "\n Trait items: "; + if (!has_trait_items ()) + { + str += "none"; + } + else + { + for (const auto &item : trait_items) + { + // DEBUG: null pointer check + if (item == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "trait item in trait."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + item->as_string (); + } + } + + return str; +} + +std::string +Union::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "union " + union_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in union."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + // struct fields + str += "\n Struct fields (variants): "; + if (variants.empty ()) + { + str += "none"; + } + else + { + for (const auto &field : variants) + str += "\n " + field.as_string (); + } + + return str; +} + +std::string +Function::as_string () const +{ + std::string str = VisItem::as_string () + "\n"; + std::string qstr = qualifiers.as_string (); + if ("" != qstr) + str += qstr + " "; + + if (has_return_type ()) + { + // DEBUG: null pointer check + if (return_type == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer return " + "type in function."); + return "NULL_POINTER_MARK"; + } + + str += return_type->as_string () + " "; + } + else + { + str += "void "; + } + + str += function_name; + + if (has_generics ()) + { + str += "<"; + + auto i = generic_params.begin (); + auto e = generic_params.end (); + + // DEBUG: null pointer check + if (i == e) + { + rust_debug ("something really terrible has gone wrong - null pointer " + "generic param in function item."); + return "NULL_POINTER_MARK"; + } + + for (; i != e; i++) + { + str += (*i)->as_string (); + if (e != i + 1) + str += ", "; + } + str += ">"; + } + + if (has_function_params ()) + { + auto i = function_params.begin (); + auto e = function_params.end (); + str += "("; + for (; i != e; i++) + { + str += (*i).as_string (); + if (e != i + 1) + str += ", "; + } + str += ")"; + } + else + { + str += "()"; + } + + if (has_where_clause ()) + str += " where " + where_clause.as_string (); + + str += "\n"; + + // DEBUG: null pointer check + if (function_body == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer function " + "body in function."); + return "NULL_POINTER_MARK"; + } + str += function_body->as_string () + "\n"; + + return str; +} + +std::string +WhereClause::as_string () const +{ + // just print where clause items, don't mention "where" or "where clause" + std::string str; + + if (where_clause_items.empty ()) + { + str = "none"; + } + else + { + for (const auto &item : where_clause_items) + str += "\n " + item->as_string (); + } + + return str; +} + +std::string +BlockExpr::as_string () const +{ + std::string istr = indent_spaces (enter); + std::string str = istr + "BlockExpr:\n" + istr; + + // get outer attributes + str += append_attributes (outer_attrs, OUTER); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + // statements + str += "\n" + indent_spaces (stay) + "statements: "; + if (statements.empty ()) + { + str += "none"; + } + else + { + for (const auto &stmt : statements) + { + // DEBUG: null pointer check + if (stmt == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "stmt in block expr."); + return "NULL_POINTER_MARK"; + } + + str += "\n" + indent_spaces (stay) + stmt->as_string (); + } + } + + // final expression + str += "\n" + indent_spaces (stay) + "final expression: "; + if (expr == nullptr) + str += "none"; + else + str += "\n" + expr->as_string (); + + str += "\n" + indent_spaces (out); + return str; +} + +std::string +TraitImpl::as_string () const +{ + std::string str = VisItem::as_string (); + + if (has_unsafe) + str += "unsafe "; + + str += "impl "; + + // generic params + str += "\n Generic params: "; + if (!has_generics ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + str += "\n " + param->as_string (); + } + + str += "\n Has exclam: "; + if (has_exclam) + str += "true"; + else + str += "false"; + + str += "\n TypePath (to trait): " + trait_path.as_string (); + + str += "\n Type (struct to impl on): " + trait_type->as_string (); + + str += "\n Where clause: "; + if (!has_where_clause ()) + str += "none"; + else + str += where_clause.as_string (); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + str += "\n trait impl items: "; + if (!has_impl_items ()) + { + str += "none"; + } + else + { + for (const auto &item : impl_items) + str += "\n " + item->as_string (); + } + + return str; +} + +std::string +TypeAlias::as_string () const +{ + std::string str = VisItem::as_string (); + + str += " " + new_type_name; + + // generic params + str += "\n Generic params: "; + if (!has_generics ()) + { + str += "none"; + } + else + { + auto i = generic_params.begin (); + auto e = generic_params.end (); + + for (; i != e; i++) + { + str += (*i)->as_string (); + if (e != i + 1) + str += ", "; + } + } + + str += "\n Where clause: "; + if (!has_where_clause ()) + str += "none"; + else + str += where_clause.as_string (); + + str += "\n Type: " + existing_type->as_string (); + + return str; +} + +std::string +ExternBlock::as_string () const +{ + std::string str = VisItem::as_string (); + + str += "extern "; + if (has_abi ()) + str += "\"" + abi + "\" "; + + str += append_attributes (inner_attrs, INNER); + + str += "\n external items: "; + if (!has_extern_items ()) + { + str += "none"; + } + else + { + for (const auto &item : extern_items) + str += "\n " + item->as_string (); + } + + return str; +} + +std::string +MacroRule::as_string () const +{ + std::string str ("Macro rule: "); + + str += "\n Matcher: \n "; + str += matcher.as_string (); + + str += "\n Transcriber: \n "; + str += transcriber.as_string (); + + return str; +} + +std::string +MacroRulesDefinition::as_string () const +{ + std::string str; + + // get outer attrs + str += append_attributes (outer_attrs, OUTER); + + str += "macro_rules!"; + + str += rule_name; + + str += "\n Macro rules: "; + if (rules.empty ()) + { + str += "none"; + } + else + { + for (const auto &rule : rules) + str += "\n " + rule.as_string (); + } + + str += "\n Delim type: "; + switch (delim_type) + { + case PARENS: + str += "parentheses"; + break; + case SQUARE: + str += "square"; + break; + case CURLY: + str += "curly"; + break; + default: + return "ERROR_MARK_STRING - delim type in macro invocation"; + } + + return str; +} + +std::string +MacroInvocation::as_string () const +{ + std::string str = "MacroInvocation: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n " + invoc_data.as_string (); + + str += "\n has semicolon: "; + str += has_semicolon () ? "true" : "false"; + + return str; +} + +std::string +MacroInvocData::as_string () const +{ + return path.as_string () + "!" + token_tree.as_string (); +} + +std::string +PathInExpression::as_string () const +{ + std::string str; + + if (has_opening_scope_resolution) + str = "::"; + + return str + PathPattern::as_string (); +} + +std::string +ExprStmtWithBlock::as_string () const +{ + std::string str = indent_spaces (enter) + "ExprStmtWithBlock: \n"; + + if (expr == nullptr) + { + str += "none (this should not happen and is an error)"; + } + else + { + indent_spaces (enter); + str += expr->as_string (); + indent_spaces (out); + } + + indent_spaces (out); + return str; +} + +std::string +ClosureParam::as_string () const +{ + std::string str (pattern->as_string ()); + + if (has_type_given ()) + str += " : " + type->as_string (); + + return str; +} + +std::string +ClosureExpr::as_string () const +{ + std::string str = "ClosureExpr:"; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Has move: "; + if (has_move) + str += "true"; + else + str += "false"; + + str += "\n Params: "; + if (params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : params) + str += "\n " + param.as_string (); + } + + return str; +} + +std::string +ClosureExprInnerTyped::as_string () const +{ + std::string str = ClosureExpr::as_string (); + + str += "\n Return type: " + return_type->as_string (); + + str += "\n Body: " + expr->as_string (); + + return str; +} + +std::string +PathPattern::as_string () const +{ + std::string str; + + for (const auto &segment : segments) + str += segment.as_string () + "::"; + + // basically a hack - remove last two characters of string (remove final ::) + str.erase (str.length () - 2); + + return str; +} + +std::string +QualifiedPathType::as_string () const +{ + std::string str ("<"); + str += type_to_invoke_on->as_string (); + + if (has_as_clause ()) + str += " as " + trait_path.as_string (); + + return str + ">"; +} + +std::string +QualifiedPathInExpression::as_string () const +{ + return path_type.as_string () + "::" + PathPattern::as_string (); +} + +std::string +BorrowExpr::as_string () const +{ + /* TODO: find way to incorporate outer attrs - may have to represent in + * different style (i.e. something more like BorrowExpr: \n outer attrs) */ + + std::string str ("&"); + + if (double_borrow) + str += "&"; + + if (is_mut) + str += "mut "; + + str += main_or_left_expr->as_string (); + + return str; +} + +std::string +ReturnExpr::as_string () const +{ + /* TODO: find way to incorporate outer attrs - may have to represent in + * different style (i.e. something more like BorrowExpr: \n outer attrs) */ + + std::string str ("return "); + + if (has_returned_expr ()) + str += return_expr->as_string (); + + return str; +} + +std::string +GroupedExpr::as_string () const +{ + std::string str ("Grouped expr:"); + + // outer attrs + str += append_attributes (outer_attrs, OUTER); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + str += "\n Expr in parens: " + expr_in_parens->as_string (); + + return str; +} + +std::string +RangeToExpr::as_string () const +{ + return ".." + to->as_string (); +} + +std::string +ContinueExpr::as_string () const +{ + // TODO: rewrite format to allow outer attributes + std::string str ("continue "); + + if (has_label ()) + str += label.as_string (); + + return str; +} + +std::string +NegationExpr::as_string () const +{ + // TODO: rewrite formula to allow outer attributes + std::string str; + + switch (expr_type) + { + case NegationOperator::NEGATE: + str = "-"; + break; + case NegationOperator::NOT: + str = "!"; + break; + default: + return "ERROR_MARK_STRING - negation expr"; + } + + str += main_or_left_expr->as_string (); + + return str; +} + +std::string +RangeFromExpr::as_string () const +{ + return from->as_string () + ".."; +} + +std::string +RangeFullExpr::as_string () const +{ + return ".."; +} + +std::string +ArrayIndexExpr::as_string () const +{ + // TODO: rewrite formula to allow outer attributes + return array_expr->as_string () + "[" + index_expr->as_string () + "]"; +} + +std::string +AssignmentExpr::as_string () const +{ + std::string str ("AssignmentExpr: "); + + if (main_or_left_expr == nullptr || right_expr == nullptr) + { + str += "error (either or both expressions are null)"; + } + else + { + // left expr + str += "\n left: " + main_or_left_expr->as_string (); + + // right expr + str += "\n right: " + right_expr->as_string (); + } + + return str; +} + +std::string +AsyncBlockExpr::as_string () const +{ + std::string str = "AsyncBlockExpr: "; + + // get outer attributes + // str += "\n " + Expr::as_string (); + str += append_attributes (outer_attrs, OUTER); + + str += "\n Has move: "; + str += has_move ? "true" : "false"; + + return str + "\n" + block_expr->as_string (); +} + +std::string +ComparisonExpr::as_string () const +{ + // TODO: rewrite to better reflect non-literal expressions + std::string str (main_or_left_expr->as_string ()); + + switch (expr_type) + { + case ComparisonOperator::EQUAL: + str += " == "; + break; + case ComparisonOperator::NOT_EQUAL: + str += " != "; + break; + case ComparisonOperator::GREATER_THAN: + str += " > "; + break; + case ComparisonOperator::LESS_THAN: + str += " < "; + break; + case ComparisonOperator::GREATER_OR_EQUAL: + str += " >= "; + break; + case ComparisonOperator::LESS_OR_EQUAL: + str += " <= "; + break; + default: + return "ERROR_MARK_STRING - comparison expr"; + } + + str += right_expr->as_string (); + + return str; +} + +std::string +MethodCallExpr::as_string () const +{ + std::string str = "MethodCallExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Object (receiver) expr: \n"; + str += receiver->as_string (); + + str += "\n Method path segment: \n"; + str += method_name.as_string (); + + str += "\n Call params:"; + if (params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : params) + { + if (param == nullptr) + return "ERROR_MARK_STRING - method call expr param is null"; + + str += "\n " + param->as_string (); + } + } + + return str; +} + +std::string +TupleIndexExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + return tuple_expr->as_string () + "." + std::to_string (tuple_index); +} + +std::string +DereferenceExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + return "*" + main_or_left_expr->as_string (); +} + +std::string +FieldAccessExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + return receiver->as_string () + "." + field; +} + +std::string +LazyBooleanExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + std::string str (main_or_left_expr->as_string ()); + + switch (expr_type) + { + case LazyBooleanOperator::LOGICAL_OR: + str += " || "; + break; + case LazyBooleanOperator::LOGICAL_AND: + str += " && "; + break; + default: + return "ERROR_MARK_STRING - lazy boolean expr out of bounds"; + } + + str += right_expr->as_string (); + + return str; +} + +std::string +RangeFromToExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + return from->as_string () + ".." + to->as_string (); +} + +std::string +RangeToInclExpr::as_string () const +{ + // TODO: rewrite dump to better reflect non-literal exprs + return "..=" + to->as_string (); +} + +std::string +UnsafeBlockExpr::as_string () const +{ + std::string str = "UnsafeBlockExpr:" + indent_spaces (enter); + + // get outer attributes + str += append_attributes (outer_attrs, OUTER); + + str += indent_spaces (stay) + expr->as_string () + "\n" + indent_spaces (out); + + return str; +} + +std::string +ClosureExprInner::as_string () const +{ + std::string str = ClosureExpr::as_string (); + + str += "\n Expression: " + closure_inner->as_string (); + + return str; +} + +std::string +IfExpr::as_string () const +{ + std::string str = "IfExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Condition expr: " + condition->as_string (); + + str += "\n If block expr: " + if_block->as_string (); + + return str; +} + +std::string +IfExprConseqElse::as_string () const +{ + std::string str = IfExpr::as_string (); + + str += "\n Else block expr: " + else_block->as_string (); + + return str; +} + +std::string +IfExprConseqIf::as_string () const +{ + std::string str = IfExpr::as_string (); + + str += "\n Else if expr: \n " + conseq_if_expr->as_string (); + + return str; +} + +std::string +IfExprConseqIfLet::as_string () const +{ + std::string str = IfExpr::as_string (); + + str += "\n Else if let expr: \n " + if_let_expr->as_string (); + + return str; +} + +std::string +IfLetExpr::as_string () const +{ + std::string str = "IfLetExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Condition match arm patterns: "; + if (match_arm_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &pattern : match_arm_patterns) + str += "\n " + pattern->as_string (); + } + + str += "\n Scrutinee expr: " + value->as_string (); + + str += "\n If let block expr: " + if_block->as_string (); + + return str; +} + +std::string +IfLetExprConseqElse::as_string () const +{ + std::string str = IfLetExpr::as_string (); + + str += "\n Else block expr: " + else_block->as_string (); + + return str; +} + +std::string +IfLetExprConseqIf::as_string () const +{ + std::string str = IfLetExpr::as_string (); + + str += "\n Else if expr: \n " + if_expr->as_string (); + + return str; +} + +std::string +IfLetExprConseqIfLet::as_string () const +{ + std::string str = IfLetExpr::as_string (); + + str += "\n Else if let expr: \n " + if_let_expr->as_string (); + + return str; +} + +std::string +RangeFromToInclExpr::as_string () const +{ + // TODO: rewrite to allow dumps with non-literal exprs + return from->as_string () + "..=" + to->as_string (); +} + +std::string +ErrorPropagationExpr::as_string () const +{ + // TODO: rewrite to allow dumps with non-literal exprs + return main_or_left_expr->as_string () + "?"; +} + +std::string +CompoundAssignmentExpr::as_string () const +{ + std::string operator_str; + operator_str.reserve (1); + + // get operator string + switch (expr_type) + { + case CompoundAssignmentOperator::ADD: + operator_str = "+"; + break; + case CompoundAssignmentOperator::SUBTRACT: + operator_str = "-"; + break; + case CompoundAssignmentOperator::MULTIPLY: + operator_str = "*"; + break; + case CompoundAssignmentOperator::DIVIDE: + operator_str = "/"; + break; + case CompoundAssignmentOperator::MODULUS: + operator_str = "%"; + break; + case CompoundAssignmentOperator::BITWISE_AND: + operator_str = "&"; + break; + case CompoundAssignmentOperator::BITWISE_OR: + operator_str = "|"; + break; + case CompoundAssignmentOperator::BITWISE_XOR: + operator_str = "^"; + break; + case CompoundAssignmentOperator::LEFT_SHIFT: + operator_str = "<<"; + break; + case CompoundAssignmentOperator::RIGHT_SHIFT: + operator_str = ">>"; + break; + default: + operator_str = "invalid operator. wtf"; + break; + } + + operator_str += "="; + + std::string str ("CompoundAssignmentExpr: "); + if (main_or_left_expr == nullptr || right_expr == nullptr) + { + str += "error. this is probably a parsing failure."; + } + else + { + str += "\n left: " + main_or_left_expr->as_string (); + str += "\n right: " + right_expr->as_string (); + str += "\n operator: " + operator_str; + } + + return str; +} + +std::string +ArithmeticOrLogicalExpr::as_string () const +{ + std::string operator_str; + operator_str.reserve (1); + + // get operator string + switch (expr_type) + { + case ArithmeticOrLogicalOperator::ADD: + operator_str = "+"; + break; + case ArithmeticOrLogicalOperator::SUBTRACT: + operator_str = "-"; + break; + case ArithmeticOrLogicalOperator::MULTIPLY: + operator_str = "*"; + break; + case ArithmeticOrLogicalOperator::DIVIDE: + operator_str = "/"; + break; + case ArithmeticOrLogicalOperator::MODULUS: + operator_str = "%"; + break; + case ArithmeticOrLogicalOperator::BITWISE_AND: + operator_str = "&"; + break; + case ArithmeticOrLogicalOperator::BITWISE_OR: + operator_str = "|"; + break; + case ArithmeticOrLogicalOperator::BITWISE_XOR: + operator_str = "^"; + break; + case ArithmeticOrLogicalOperator::LEFT_SHIFT: + operator_str = "<<"; + break; + case ArithmeticOrLogicalOperator::RIGHT_SHIFT: + operator_str = ">>"; + break; + default: + operator_str = "invalid operator. wtf"; + break; + } + + std::string str ("ArithmeticOrLogicalExpr: "); + if (main_or_left_expr == nullptr || right_expr == nullptr) + { + str += "error. this is probably a parsing failure."; + } + else + { + str += main_or_left_expr->as_string () + " "; + str += operator_str + " "; + str += right_expr->as_string (); + } + + return str; +} + +std::string +CallExpr::as_string () const +{ + std::string str = "CallExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Function expr: "; + str += function->as_string (); + + str += "\n Call params:"; + if (!has_params ()) + { + str += "none"; + } + else + { + for (const auto ¶m : params) + { + if (param == nullptr) + return "ERROR_MARK_STRING - call expr param is null"; + + str += "\n " + param->as_string (); + } + } + + return str; +} + +std::string +WhileLoopExpr::as_string () const +{ + std::string str = "WhileLoopExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Label: "; + if (!has_loop_label ()) + str += "none"; + else + str += loop_label.as_string (); + + str += "\n Conditional expr: " + condition->as_string (); + + str += "\n Loop block: " + loop_block->as_string (); + + return str; +} + +std::string +WhileLetLoopExpr::as_string () const +{ + std::string str = "WhileLetLoopExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Label: "; + if (!has_loop_label ()) + str += "none"; + else + str += loop_label.as_string (); + + str += "\n Match arm patterns: "; + if (match_arm_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &pattern : match_arm_patterns) + str += "\n " + pattern->as_string (); + } + + str += "\n Scrutinee expr: " + scrutinee->as_string (); + + str += "\n Loop block: " + loop_block->as_string (); + + return str; +} + +std::string +LoopExpr::as_string () const +{ + std::string str = "LoopExpr: (infinite loop)"; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Label: "; + if (!has_loop_label ()) + str += "none"; + else + str += loop_label.as_string (); + + str += "\n Loop block: " + loop_block->as_string (); + + return str; +} + +std::string +ArrayExpr::as_string () const +{ + std::string str = "ArrayExpr:"; + + str += append_attributes (outer_attrs, OUTER); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + str += "\n Array elems: "; + str += internal_elements->as_string (); + + return str; +} + +std::string +AwaitExpr::as_string () const +{ + // TODO: rewrite dump to allow non-literal exprs + return awaited_expr->as_string () + ".await"; +} + +std::string +BreakExpr::as_string () const +{ + // TODO: rewrite dump to allow outer attrs, non-literal exprs + std::string str ("break "); + + if (has_label ()) + str += label.as_string () + " "; + + if (has_break_expr ()) + str += break_expr->as_string (); + + return str; +} + +std::string +LoopLabel::as_string () const +{ + return label.as_string () + ": (label) "; +} + +std::string +MatchArm::as_string () const +{ + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\nPatterns: "; + if (match_arm_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &pattern : match_arm_patterns) + str += "\n " + pattern->as_string (); + } + + str += "\nGuard expr: "; + if (!has_match_arm_guard ()) + str += "none"; + else + str += guard_expr->as_string (); + + return str; +} + +std::string +MatchCase::as_string () const +{ + std::string str ("MatchCase: (match arm) "); + + str += "\n Match arm matcher: \n" + arm.as_string (); + str += "\n Expr: " + expr->as_string (); + + return str; +} + +std::string +MatchExpr::as_string () const +{ + std::string str ("MatchExpr:"); + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Scrutinee expr: " + branch_value->as_string (); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + // match arms + str += "\n Match arms: "; + if (match_arms.empty ()) + { + str += "none"; + } + else + { + for (const auto &arm : match_arms) + str += "\n " + arm.as_string (); + } + + return str; +} + +std::string +TupleExpr::as_string () const +{ + std::string str ("TupleExpr:"); + + str += append_attributes (outer_attrs, OUTER); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + str += "\n Tuple elements: "; + if (tuple_elems.empty ()) + { + str += "none"; + } + else + { + for (const auto &elem : tuple_elems) + str += "\n " + elem->as_string (); + } + + return str; +} + +std::string +ExprStmtWithoutBlock::as_string () const +{ + std::string str ("ExprStmtWithoutBlock:\n"); + indent_spaces (enter); + str += indent_spaces (stay); + + if (expr == nullptr) + str += "none (this shouldn't happen and is probably an error)"; + else + str += expr->as_string (); + indent_spaces (out); + + return str; +} + +std::string +FunctionParam::as_string () const +{ + // TODO: rewrite dump to allow non-literal types + return param_name->as_string () + " : " + type->as_string (); +} + +std::string +FunctionQualifiers::as_string () const +{ + std::string str; + + switch (const_status) + { + case NONE: + // do nothing + break; + case CONST_FN: + str += "const "; + break; + case ASYNC_FN: + str += "async "; + break; + default: + return "ERROR_MARK_STRING: async-const status failure"; + } + + if (has_unsafe) + str += "unsafe "; + + if (has_extern) + { + str += "extern"; + if (extern_abi != "") + str += " \"" + extern_abi + "\""; + } + + return str; +} + +std::string +TraitBound::as_string () const +{ + std::string str ("TraitBound:"); + + str += "\n Has opening question mark: "; + if (opening_question_mark) + str += "true"; + else + str += "false"; + + str += "\n For lifetimes: "; + if (!has_for_lifetimes ()) + { + str += "none"; + } + else + { + for (const auto &lifetime : for_lifetimes) + str += "\n " + lifetime.as_string (); + } + + str += "\n Type path: " + type_path.as_string (); + + return str; +} + +std::string +MacroMatcher::as_string () const +{ + std::string str ("Macro matcher: "); + + str += "\n Delim type: "; + + switch (delim_type) + { + case PARENS: + str += "parentheses"; + break; + case SQUARE: + str += "square"; + break; + case CURLY: + str += "curly"; + break; + default: + return "ERROR_MARK_STRING - macro matcher delim"; + } + + str += "\n Matches: "; + + if (matches.empty ()) + { + str += "none"; + } + else + { + for (const auto &match : matches) + str += "\n " + match->as_string (); + } + + return str; +} + +std::string +LifetimeParam::as_string () const +{ + std::string str ("LifetimeParam: "); + + str += "\n Outer attribute: "; + if (!has_outer_attribute ()) + str += "none"; + else + str += outer_attr.as_string (); + + str += "\n Lifetime: " + lifetime.as_string (); + + str += "\n Lifetime bounds: "; + if (!has_lifetime_bounds ()) + { + str += "none"; + } + else + { + for (const auto &bound : lifetime_bounds) + str += "\n " + bound.as_string (); + } + + return str; +} + +std::string +ConstGenericParam::as_string () const +{ + std::string str ("ConstGenericParam: "); + str += "const " + name + ": " + type->as_string (); + + if (has_default_value ()) + str += " = " + get_default_value ().as_string (); + + return str; +} + +std::string +MacroMatchFragment::as_string () const +{ + return "$" + ident + ": " + frag_spec.as_string (); +} + +std::string +QualifiedPathInType::as_string () const +{ + /* TODO: this may need adjusting if segments (e.g. with functions) can't be + * literalised */ + std::string str = path_type.as_string (); + + for (const auto &segment : segments) + str += "::" + segment->as_string (); + + return str; +} + +std::string +MacroMatchRepetition::as_string () const +{ + std::string str ("Macro match repetition: "); + + str += "\n Matches: "; + if (matches.empty ()) + { + str += "none"; + } + else + { + for (const auto &match : matches) + str += "\n " + match->as_string (); + } + + str += "\n Sep: "; + if (!has_sep ()) + str += "none"; + else + str += sep->as_string (); + + str += "\n Op: "; + switch (op) + { + case ANY: + str += "*"; + break; + case ONE_OR_MORE: + str += "+"; + break; + case ZERO_OR_ONE: + str += "?"; + break; + case NONE: + str += "no op? shouldn't be allowed"; + break; + default: + return "ERROR_MARK_STRING - unknown op in macro match repetition"; + } + + return str; +} + +std::string +Lifetime::as_string () const +{ + if (is_error ()) + return "error lifetime"; + + switch (lifetime_type) + { + case NAMED: + return "'" + lifetime_name; + case STATIC: + return "'static"; + case WILDCARD: + return "'_"; + default: + return "ERROR-MARK-STRING: lifetime type failure"; + } +} + +std::string +TypePath::as_string () const +{ + /* TODO: this may need to be rewritten if a segment (e.g. function) can't be + * literalised */ + std::string str; + + if (has_opening_scope_resolution) + str = "::"; + + for (const auto &segment : segments) + str += segment->as_string () + "::"; + + // kinda hack - remove last 2 '::' characters + str.erase (str.length () - 2); + + return str; +} + +std::string +TypeParam::as_string () const +{ + std::string str ("TypeParam: "); + + str += "\n Outer attribute: "; + if (!has_outer_attribute ()) + str += "none"; + else + str += outer_attr.as_string (); + + str += "\n Identifier: " + type_representation; + + str += "\n Type param bounds: "; + if (!has_type_param_bounds ()) + { + str += "none"; + } + else + { + for (const auto &bound : type_param_bounds) + str += "\n " + bound->as_string (); + } + + str += "\n Type: "; + if (!has_type ()) + str += "none"; + else + str += type->as_string (); + + return str; +} + +SimplePath +PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const +{ + if (!has_segments ()) + return SimplePath::create_empty (); + + // create vector of reserved size (to minimise reallocations) + std::vector simple_segments; + simple_segments.reserve (segments.size ()); + + for (const auto &segment : segments) + { + // return empty path if doesn't meet simple path segment requirements + if (segment.is_error () || segment.has_generic_args () + || segment.as_string () == "Self") + return SimplePath::create_empty (); + + // create segment and add to vector + std::string segment_str = segment.as_string (); + simple_segments.push_back ( + SimplePathSegment (std::move (segment_str), segment.get_locus ())); + } + + // kind of a HACK to get locus depending on opening scope resolution + Location locus = Linemap::unknown_location (); + if (with_opening_scope_resolution) + locus = simple_segments[0].get_locus () - 2; // minus 2 chars for :: + else + locus = simple_segments[0].get_locus (); + // FIXME: this hack probably doesn't actually work + + return SimplePath (std::move (simple_segments), with_opening_scope_resolution, + locus); +} + +SimplePath +TypePath::as_simple_path () const +{ + if (segments.empty ()) + return SimplePath::create_empty (); + + // create vector of reserved size (to minimise reallocations) + std::vector simple_segments; + simple_segments.reserve (segments.size ()); + + for (const auto &segment : segments) + { + // return empty path if doesn't meet simple path segment requirements + if (segment == nullptr || segment->is_error () + || !segment->is_ident_only () || segment->as_string () == "Self") + return SimplePath::create_empty (); + + // create segment and add to vector + std::string segment_str = segment->as_string (); + simple_segments.push_back ( + SimplePathSegment (std::move (segment_str), segment->get_locus ())); + } + + return SimplePath (std::move (simple_segments), has_opening_scope_resolution, + locus); +} + +std::string +PathExprSegment::as_string () const +{ + // TODO: rewrite dump to work with non-literalisable types + std::string ident_str = segment_name.as_string (); + if (has_generic_args ()) + ident_str += "::<" + generic_args.as_string () + ">"; + + return ident_str; +} + +std::string +GenericArgs::as_string () const +{ + std::string args; + + // lifetime args + if (!lifetime_args.empty ()) + { + auto i = lifetime_args.begin (); + auto e = lifetime_args.end (); + + for (; i != e; i++) + { + args += (*i).as_string (); + if (e != i + 1) + args += ", "; + } + } + + // type args + if (!generic_args.empty ()) + { + auto i = generic_args.begin (); + auto e = generic_args.end (); + + for (; i != e; i++) + { + args += (*i).as_string (); + if (e != i + 1) + args += ", "; + } + } + + // binding args + if (!binding_args.empty ()) + { + auto i = binding_args.begin (); + auto e = binding_args.end (); + + for (; i != e; i++) + { + args += (*i).as_string (); + if (e != i + 1) + args += ", "; + } + } + + return args; +} + +std::string +GenericArgsBinding::as_string () const +{ + // TODO: rewrite to work with non-literalisable types + return identifier + " = " + type->as_string (); +} + +std::string +ForLoopExpr::as_string () const +{ + std::string str = "ForLoopExpr: "; + + str += append_attributes (outer_attrs, OUTER); + + str += "\n Label: "; + if (!has_loop_label ()) + str += "none"; + else + str += loop_label.as_string (); + + str += "\n Pattern: " + pattern->as_string (); + + str += "\n Iterator expr: " + iterator_expr->as_string (); + + str += "\n Loop block: " + loop_block->as_string (); + + return str; +} + +std::string +RangePattern::as_string () const +{ + // TODO: maybe rewrite to work with non-linearisable bounds + if (has_ellipsis_syntax) + return lower->as_string () + "..." + upper->as_string (); + else + return lower->as_string () + "..=" + upper->as_string (); +} + +std::string +RangePatternBoundLiteral::as_string () const +{ + std::string str; + + if (has_minus) + str += "-"; + + str += literal.as_string (); + + return str; +} + +std::string +SlicePattern::as_string () const +{ + std::string str ("SlicePattern: "); + + for (const auto &pattern : items) + str += "\n " + pattern->as_string (); + + return str; +} + +std::string +TuplePatternItemsMultiple::as_string () const +{ + std::string str; + + for (const auto &pattern : patterns) + str += "\n " + pattern->as_string (); + + return str; +} + +std::string +TuplePatternItemsRanged::as_string () const +{ + std::string str; + + str += "\n Lower patterns: "; + if (lower_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &lower : lower_patterns) + str += "\n " + lower->as_string (); + } + + str += "\n Upper patterns: "; + if (upper_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &upper : upper_patterns) + str += "\n " + upper->as_string (); + } + + return str; +} + +std::string +TuplePattern::as_string () const +{ + return "TuplePattern: " + items->as_string (); +} + +std::string +StructPatternField::as_string () const +{ + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + return str; +} + +std::string +StructPatternFieldIdent::as_string () const +{ + std::string str = StructPatternField::as_string (); + + str += "\n"; + + if (has_ref) + str += "ref "; + + if (has_mut) + str += "mut "; + + str += ident; + + return str; +} + +std::string +StructPatternFieldTuplePat::as_string () const +{ + // TODO: maybe rewrite to work with non-linearisable patterns + std::string str = StructPatternField::as_string (); + + str += "\n"; + + str += std::to_string (index) + " : " + tuple_pattern->as_string (); + + return str; +} + +std::string +StructPatternFieldIdentPat::as_string () const +{ + // TODO: maybe rewrite to work with non-linearisable patterns + std::string str = StructPatternField::as_string (); + + str += "\n"; + + str += ident + " : " + ident_pattern->as_string (); + + return str; +} + +std::string +StructPatternElements::as_string () const +{ + std::string str ("\n Fields: "); + + if (!has_struct_pattern_fields ()) + { + str += "none"; + } + else + { + for (const auto &field : fields) + str += "\n " + field->as_string (); + } + + str += "\n Etc: "; + if (has_struct_pattern_etc) + str += "true"; + else + str += "false"; + + return str; +} + +std::string +StructPattern::as_string () const +{ + std::string str ("StructPattern: \n Path: "); + + str += path.as_string (); + + str += "\n Struct pattern elems: "; + if (!has_struct_pattern_elems ()) + str += "none"; + else + str += elems.as_string (); + + return str; +} + +std::string +LiteralPattern::as_string () const +{ + return lit.as_string (); +} + +std::string +ReferencePattern::as_string () const +{ + // TODO: maybe rewrite to work with non-linearisable patterns + std::string str ("&"); + + if (has_two_amps) + str += "&"; + + if (is_mut) + str += "mut "; + + str += pattern->as_string (); + + return str; +} + +std::string +IdentifierPattern::as_string () const +{ + // TODO: maybe rewrite to work with non-linearisable patterns + std::string str; + + if (is_ref) + str += "ref "; + + if (is_mut) + str += "mut "; + + str += variable_ident; + + if (has_pattern_to_bind ()) + str += " @ " + to_bind->as_string (); + + return str; +} + +std::string +TupleStructItemsNoRange::as_string () const +{ + std::string str; + + for (const auto &pattern : patterns) + str += "\n " + pattern->as_string (); + + return str; +} + +std::string +TupleStructItemsRange::as_string () const +{ + std::string str ("\n Lower patterns: "); + + if (lower_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &lower : lower_patterns) + str += "\n " + lower->as_string (); + } + + str += "\n Upper patterns: "; + if (upper_patterns.empty ()) + { + str += "none"; + } + else + { + for (const auto &upper : upper_patterns) + str += "\n " + upper->as_string (); + } + + return str; +} + +std::string +TupleStructPattern::as_string () const +{ + std::string str ("TupleStructPattern: \n Path: "); + + str += path.as_string (); + + str += "\n Tuple struct items: " + items->as_string (); + + return str; +} + +std::string +LetStmt::as_string () const +{ + // TODO: rewrite to work with non-linearisable types and exprs + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\n" + indent_spaces (stay) + "let " + variables_pattern->as_string (); + + if (has_type ()) + str += " : " + type->as_string (); + + if (has_init_expr ()) + str += " = " + init_expr->as_string (); + + return str; +} + +// hopefully definition here will prevent circular dependency issue +TraitBound * +TypePath::to_trait_bound (bool in_parens) const +{ + return new TraitBound (TypePath (*this), get_locus (), in_parens); +} + +std::string +InferredType::as_string () const +{ + return "_ (inferred)"; +} + +std::string +TypeCastExpr::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs and types + return main_or_left_expr->as_string () + " as " + + type_to_convert_to->as_string (); +} + +std::string +ImplTraitType::as_string () const +{ + std::string str ("ImplTraitType: \n TypeParamBounds: "); + + if (type_param_bounds.empty ()) + { + str += "none"; + } + else + { + for (const auto &bound : type_param_bounds) + str += "\n " + bound->as_string (); + } + + return str; +} + +std::string +ReferenceType::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + std::string str ("&"); + + if (has_lifetime ()) + str += lifetime.as_string () + " "; + + if (has_mut) + str += "mut "; + + str += type->as_string (); + + return str; +} + +std::string +RawPointerType::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + std::string str ("*"); + + switch (pointer_type) + { + case MUT: + str += "mut "; + break; + case CONST: + str += "const "; + break; + default: + return "ERROR_MARK_STRING - unknown pointer type in raw pointer type"; + } + + str += type->as_string (); + + return str; +} + +std::string +TraitObjectType::as_string () const +{ + std::string str ("TraitObjectType: \n Has dyn dispatch: "); + + if (has_dyn) + str += "true"; + else + str += "false"; + + str += "\n TypeParamBounds: "; + if (type_param_bounds.empty ()) + { + str += "none"; + } + else + { + for (const auto &bound : type_param_bounds) + str += "\n " + bound->as_string (); + } + + return str; +} + +std::string +BareFunctionType::as_string () const +{ + std::string str ("BareFunctionType: \n For lifetimes: "); + + if (!has_for_lifetimes ()) + { + str += "none"; + } + else + { + for (const auto &for_lifetime : for_lifetimes) + str += "\n " + for_lifetime.as_string (); + } + + str += "\n Qualifiers: " + function_qualifiers.as_string (); + + str += "\n Params: "; + if (params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : params) + str += "\n " + param.as_string (); + } + + str += "\n Is variadic: "; + if (is_variadic) + str += "true"; + else + str += "false"; + + str += "\n Return type: "; + if (!has_return_type ()) + str += "none (void)"; + else + str += return_type->as_string (); + + return str; +} + +std::string +ImplTraitTypeOneBound::as_string () const +{ + std::string str ("ImplTraitTypeOneBound: \n TraitBound: "); + + return str + trait_bound.as_string (); +} + +std::string +TypePathSegmentGeneric::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">"; +} + +std::string +TraitObjectTypeOneBound::as_string () const +{ + std::string str ("TraitObjectTypeOneBound: \n Has dyn dispatch: "); + + if (has_dyn) + str += "true"; + else + str += "false"; + + str += "\n TraitBound: " + trait_bound.as_string (); + + return str; +} + +std::string +TypePathFunction::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + std::string str ("("); + + if (has_inputs ()) + { + auto i = inputs.begin (); + auto e = inputs.end (); + + for (; i != e; i++) + { + str += (*i)->as_string (); + if (e != i + 1) + str += ", "; + } + } + + str += ")"; + + if (has_return_type ()) + str += " -> " + return_type->as_string (); + + return str; +} + +std::string +TypePathSegmentFunction::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + return TypePathSegment::as_string () + function_path.as_string (); +} + +std::string +ArrayType::as_string () const +{ + // TODO: rewrite to work with non-linearisable types and exprs + return "[" + elem_type->as_string () + "; " + size->as_string () + "]"; +} + +std::string +SliceType::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + return "[" + elem_type->as_string () + "]"; +} + +std::string +TupleType::as_string () const +{ + // TODO: rewrite to work with non-linearisable types + std::string str ("("); + + if (!is_unit_type ()) + { + auto i = elems.begin (); + auto e = elems.end (); + + for (; i != e; i++) + { + str += (*i)->as_string (); + if (e != i + 1) + str += ", "; + } + } + + str += ")"; + + return str; +} + +std::string +StructExpr::as_string () const +{ + std::string str = append_attributes (outer_attrs, OUTER); + indent_spaces (enter); + str += "\n" + indent_spaces (stay) + "StructExpr:"; + indent_spaces (enter); + str += "\n" + indent_spaces (stay) + "PathInExpr:\n"; + str += indent_spaces (stay) + struct_name.as_string (); + indent_spaces (out); + indent_spaces (out); + return str; +} + +std::string +StructExprStruct::as_string () const +{ + // TODO: doesn't this require data from StructExpr? + std::string str ("StructExprStruct (or subclass): "); + + str += "\n Path: " + get_struct_name ().as_string (); + + // inner attributes + str += append_attributes (inner_attrs, INNER); + + return str; +} + +std::string +StructBase::as_string () const +{ + if (base_struct != nullptr) + return base_struct->as_string (); + else + return "ERROR_MARK_STRING - invalid struct base had as string applied"; +} + +std::string +StructExprFieldWithVal::as_string () const +{ + // used to get value string + return value->as_string (); +} + +std::string +StructExprFieldIdentifierValue::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + return field_name + " : " + StructExprFieldWithVal::as_string (); +} + +std::string +StructExprFieldIndexValue::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + return std::to_string (index) + " : " + StructExprFieldWithVal::as_string (); +} + +std::string +StructExprStructFields::as_string () const +{ + std::string str = StructExprStruct::as_string (); + + str += "\n Fields: "; + if (fields.empty ()) + { + str += "none"; + } + else + { + for (const auto &field : fields) + str += "\n " + field->as_string (); + } + + str += "\n Struct base: "; + if (!has_struct_base ()) + str += "none"; + else + str += struct_base.as_string (); + + return str; +} + +std::string +EnumItem::as_string () const +{ + std::string str = VisItem::as_string (); + str += variant_name; + + return str; +} + +std::string +EnumItemTuple::as_string () const +{ + std::string str = EnumItem::as_string (); + + // add tuple opening parens + str += "("; + + // tuple fields + if (has_tuple_fields ()) + { + auto i = tuple_fields.begin (); + auto e = tuple_fields.end (); + + for (; i != e; i++) + { + str += (*i).as_string (); + if (e != i + 1) + str += ", "; + } + } + + // add tuple closing parens + str += ")"; + + return str; +} + +std::string +TupleField::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + if (has_visibility ()) + str += "\n" + visibility.as_string (); + + str += " " + field_type->as_string (); + + return str; +} + +std::string +EnumItemStruct::as_string () const +{ + std::string str = EnumItem::as_string (); + + // add struct opening parens + str += "{"; + + // tuple fields + if (has_struct_fields ()) + { + auto i = struct_fields.begin (); + auto e = struct_fields.end (); + + for (; i != e; i++) + { + str += (*i).as_string (); + if (e != i + 1) + str += ", "; + } + } + + // add struct closing parens + str += "}"; + + return str; +} + +std::string +StructField::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + if (has_visibility ()) + str += "\n" + visibility.as_string (); + + str += " " + field_name + " : " + field_type->as_string (); + + return str; +} + +std::string +EnumItemDiscriminant::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + std::string str = EnumItem::as_string (); + + // add equal and expression + str += " = " + expression->as_string (); + + return str; +} + +std::string +ExternalStaticItem::as_string () const +{ + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + // start visibility on new line and with a space + str += "\n" + visibility.as_string () + " "; + + str += "static "; + + if (has_mut) + str += "mut "; + + // add name + str += item_name; + + // add type on new line + str += "\n Type: " + item_type->as_string (); + + return str; +} + +std::string +ExternalFunctionItem::as_string () const +{ + // outer attributes + std::string str = append_attributes (outer_attrs, OUTER); + + // start visibility on new line and with a space + str += "\n" + visibility.as_string () + " "; + + str += "fn "; + + // add name + str += item_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in external function item."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + // function params + str += "\n Function params: "; + if (function_params.empty () && !has_variadics) + { + str += "none"; + } + else + { + for (const auto ¶m : function_params) + str += "\n " + param.as_string (); + + if (has_variadics) + { + str += "\n variadic outer attrs: "; + if (has_variadic_outer_attrs ()) + { + for (const auto &attr : variadic_outer_attrs) + str += "\n " + attr.as_string (); + } + else + { + str += "none"; + } + str += "\n ... (variadic)"; + } + } + + // add type on new line + str += "\n (return) Type: " + + (has_return_type () ? return_type->as_string () : "()"); + + // where clause + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + return str; +} + +std::string +NamedFunctionParam::as_string () const +{ + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\n" + name; + + str += "\n Type: " + param_type->as_string (); + + return str; +} + +std::string +TraitItemFunc::as_string () const +{ + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\n" + decl.as_string (); + + str += "\n Definition (block expr): "; + if (has_definition ()) + str += block_expr->as_string (); + else + str += "none"; + + return str; +} + +std::string +TraitFunctionDecl::as_string () const +{ + std::string str = qualifiers.as_string () + "fn " + function_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in trait function decl."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Function params: "; + if (has_params ()) + { + for (const auto ¶m : function_params) + str += "\n " + param.as_string (); + } + else + { + str += "none"; + } + + str += "\n Return type: "; + if (has_return_type ()) + str += return_type->as_string (); + else + str += "none (void)"; + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + return str; +} + +std::string +TraitItemMethod::as_string () const +{ + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\n" + decl.as_string (); + + str += "\n Definition (block expr): "; + if (has_definition ()) + str += block_expr->as_string (); + else + str += "none"; + + return str; +} + +std::string +TraitMethodDecl::as_string () const +{ + std::string str = qualifiers.as_string () + "fn " + function_name; + + // generic params + str += "\n Generic params: "; + if (generic_params.empty ()) + { + str += "none"; + } + else + { + for (const auto ¶m : generic_params) + { + // DEBUG: null pointer check + if (param == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "generic param in trait function decl."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + param->as_string (); + } + } + + str += "\n Self param: " + self_param.as_string (); + + str += "\n Function params: "; + if (has_params ()) + { + for (const auto ¶m : function_params) + str += "\n " + param.as_string (); + } + else + { + str += "none"; + } + + str += "\n Return type: "; + if (has_return_type ()) + str += return_type->as_string (); + else + str += "none (void)"; + + str += "\n Where clause: "; + if (has_where_clause ()) + str += where_clause.as_string (); + else + str += "none"; + + return str; +} + +std::string +TraitItemConst::as_string () const +{ + // TODO: rewrite to work with non-linearisable exprs + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\nconst " + name + " : " + type->as_string (); + + if (has_expression ()) + str += " = " + expr->as_string (); + + return str; +} + +std::string +TraitItemType::as_string () const +{ + std::string str = append_attributes (outer_attrs, OUTER); + + str += "\ntype " + name; + + str += "\n Type param bounds: "; + if (!has_type_param_bounds ()) + { + str += "none"; + } + else + { + for (const auto &bound : type_param_bounds) + { + // DEBUG: null pointer check + if (bound == nullptr) + { + rust_debug ( + "something really terrible has gone wrong - null pointer " + "type param bound in trait item type."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + bound->as_string (); + } + } + + return str; +} + +std::string +SelfParam::as_string () const +{ + // TODO: rewrite to allow non-linearisable types + if (is_error ()) + { + return "error"; + } + else + { + if (has_type ()) + { + // type (i.e. not ref, no lifetime) + std::string str; + + if (is_mut) + str += "mut "; + + str += "self : "; + + str += type->as_string (); + + return str; + } + else if (has_lifetime ()) + { + // ref and lifetime + std::string str = "&" + lifetime.as_string () + " "; + + if (is_mut) + str += "mut "; + + str += "self"; + + return str; + } + else if (has_ref) + { + // ref with no lifetime + std::string str = "&"; + + if (is_mut) + str += " mut "; + + str += "self"; + + return str; + } + else + { + // no ref, no type + std::string str; + + if (is_mut) + str += "mut "; + + str += "self"; + + return str; + } + } +} + +std::string +ArrayElemsCopied::as_string () const +{ + // TODO: rewrite to allow non-linearisable exprs + return elem_to_copy->as_string () + "; " + num_copies->as_string (); +} + +std::string +LifetimeWhereClauseItem::as_string () const +{ + std::string str ("Lifetime: "); + + str += lifetime.as_string (); + + str += "\nLifetime bounds: "; + + for (const auto &bound : lifetime_bounds) + str += "\n " + bound.as_string (); + + return str; +} + +std::string +TypeBoundWhereClauseItem::as_string () const +{ + std::string str ("For lifetimes: "); + + if (!has_for_lifetimes ()) + { + str += "none"; + } + else + { + for (const auto &for_lifetime : for_lifetimes) + str += "\n " + for_lifetime.as_string (); + } + + str += "\nType: " + bound_type->as_string (); + + str += "\nType param bounds bounds: "; + + for (const auto &bound : type_param_bounds) + { + // debug null pointer check + if (bound == nullptr) + return "NULL_POINTER_MARK - type param bounds"; + + str += "\n " + bound->as_string (); + } + + return str; +} + +std::string +ArrayElemsValues::as_string () const +{ + std::string str; + + for (const auto &expr : values) + { + // DEBUG: null pointer check + if (expr == nullptr) + { + rust_debug ("something really terrible has gone wrong - null pointer " + "expr in array elems values."); + return "NULL_POINTER_MARK"; + } + + str += "\n " + expr->as_string (); + } + + return str; +} + +std::string +MaybeNamedParam::as_string () const +{ + // TODO: rewrite to allow using non-linearisable types in dump + std::string str; + + switch (param_kind) + { + case UNNAMED: + break; + case IDENTIFIER: + str = name + " : "; + break; + case WILDCARD: + str = "_ : "; + break; + default: + return "ERROR_MARK_STRING - maybe named param unrecognised param kind"; + } + + str += param_type->as_string (); + + return str; +} + +MetaItemInner::~MetaItemInner () = default; + +std::unique_ptr +MetaItemInner::to_meta_name_value_str () const +{ + if (is_key_value_pair ()) + { + auto converted_item = static_cast (this); + return converted_item->to_meta_name_value_str (); + } + // TODO actually parse foo = bar + return nullptr; +} + +std::string +MetaItemSeq::as_string () const +{ + std::string path_str = path.as_string () + "("; + + auto i = seq.begin (); + auto e = seq.end (); + + for (; i != e; i++) + { + path_str += (*i)->as_string (); + if (e != i + 1) + path_str += ", "; + } + + return path_str + ")"; +} + +std::string +MetaListPaths::as_string () const +{ + std::string str = ident + "("; + + auto i = paths.begin (); + auto e = paths.end (); + + for (; i != e; i++) + { + str += (*i).as_string (); + if (e != i + 1) + str += ", "; + } + + return str + ")"; +} + +std::string +MetaListNameValueStr::as_string () const +{ + std::string str = ident + "("; + + auto i = strs.begin (); + auto e = strs.end (); + + for (; i != e; i++) + { + str += (*i).as_string (); + if (e != i + 1) + str += ", "; + } + + return str + ")"; +} + +std::string +AttrInputMetaItemContainer::as_string () const +{ + std::string str = "("; + + auto i = items.begin (); + auto e = items.end (); + + for (; i != e; i++) + { + str += (*i)->as_string (); + if (e != i + 1) + str += ", "; + } + + return str + ")"; +} + +/* Override that calls the function recursively on all items contained within + * the module. */ +void +Module::add_crate_name (std::vector &names) const +{ + /* TODO: test whether module has been 'cfg'-ed out to determine whether to + * exclude it from search */ + + for (const auto &item : items) + item->add_crate_name (names); +} + +static bool +file_exists (const std::string path) +{ + // Simply check if the file exists + // FIXME: This does not work on Windows + return access (path.c_str (), F_OK) != -1; +} + +static std::string +filename_from_path_attribute (std::vector &outer_attrs) +{ + // An out-of-line module cannot have inner attributes. Additionally, the + // default name is specified as `""` so that the caller can detect the case + // of "no path given" and use the default path logic (`name.rs` or + // `name/mod.rs`). + return extract_module_path ({}, outer_attrs, ""); +} + +void +Module::process_file_path () +{ + rust_assert (kind == Module::ModuleKind::UNLOADED); + rust_assert (module_file.empty ()); + + // This corresponds to the path of the file 'including' the module. So the + // file that contains the 'mod ;' directive + std::string including_fname (outer_filename); + + std::string expected_file_path = module_name + ".rs"; + std::string expected_dir_path = "mod.rs"; + + auto dir_slash_pos = including_fname.rfind (file_separator); + std::string current_directory_name; + + // If we haven't found a file_separator, then we have to look for files in the + // current directory ('.') + if (dir_slash_pos == std::string::npos) + current_directory_name = std::string (".") + file_separator; + else + current_directory_name + = including_fname.substr (0, dir_slash_pos) + file_separator; + + // Handle inline module declarations adding path components. + for (auto const &name : module_scope) + { + current_directory_name.append (name); + current_directory_name.append (file_separator); + } + + auto path_string = filename_from_path_attribute (get_outer_attrs ()); + if (!path_string.empty ()) + { + module_file = current_directory_name + path_string; + return; + } + + // FIXME: We also have to search for + // //.rs In rustc, this is done via + // the concept of `DirOwnernship`, which is based on whether or not the + // current file is titled `mod.rs`. + + // First, we search for /.rs + std::string file_mod_path = current_directory_name + expected_file_path; + bool file_mod_found = file_exists (file_mod_path); + + // Then, search for //mod.rs + std::string dir_mod_path + = current_directory_name + module_name + file_separator + expected_dir_path; + bool dir_mod_found = file_exists (dir_mod_path); + + bool multiple_candidates_found = file_mod_found && dir_mod_found; + bool no_candidates_found = !file_mod_found && !dir_mod_found; + + if (multiple_candidates_found) + rust_error_at (locus, + "two candidates found for module %s: %s.rs and %s%smod.rs", + module_name.c_str (), module_name.c_str (), + module_name.c_str (), file_separator); + + if (no_candidates_found) + rust_error_at (locus, "no candidate found for module %s", + module_name.c_str ()); + + if (no_candidates_found || multiple_candidates_found) + return; + + module_file = std::move (file_mod_found ? file_mod_path : dir_mod_path); +} + +void +Module::load_items () +{ + process_file_path (); + + // We will already have errored out appropriately in the process_file_path () + // method + if (module_file.empty ()) + return; + + RAIIFile file_wrap (module_file.c_str ()); + Linemap *linemap = Session::get_instance ().linemap; + if (!file_wrap.ok ()) + { + rust_error_at (get_locus (), "cannot open module file %s: %m", + module_file.c_str ()); + return; + } + + rust_debug ("Attempting to parse file %s", module_file.c_str ()); + + Lexer lex (module_file.c_str (), std::move (file_wrap), linemap); + Parser parser (lex); + + // we need to parse any possible inner attributes for this module + inner_attrs = parser.parse_inner_attributes (); + auto parsed_items = parser.parse_items (); + for (const auto &error : parser.get_errors ()) + error.emit_error (); + + items = std::move (parsed_items); + kind = ModuleKind::LOADED; +} + +void +Attribute::parse_attr_to_meta_item () +{ + // only parse if has attribute input and not already parsed + if (!has_attr_input () || is_parsed_to_meta_item ()) + return; + + auto res = attr_input->parse_to_meta_item (); + std::unique_ptr converted_input (res); + + if (converted_input != nullptr) + attr_input = std::move (converted_input); +} + +AttrInputMetaItemContainer * +DelimTokenTree::parse_to_meta_item () const +{ + // must have token trees + if (token_trees.empty ()) + return nullptr; + + /* assume top-level delim token tree in attribute - convert all nested ones + * to token stream */ + std::vector> token_stream = to_token_stream (); + + AttributeParser parser (std::move (token_stream)); + std::vector> meta_items ( + parser.parse_meta_item_seq ()); + + return new AttrInputMetaItemContainer (std::move (meta_items)); +} + +std::unique_ptr +AttributeParser::parse_meta_item_inner () +{ + // if first tok not identifier, not a "special" case one + if (peek_token ()->get_id () != IDENTIFIER) + { + switch (peek_token ()->get_id ()) + { + case CHAR_LITERAL: + case STRING_LITERAL: + case BYTE_CHAR_LITERAL: + case BYTE_STRING_LITERAL: + case INT_LITERAL: + case FLOAT_LITERAL: + case TRUE_LITERAL: + case FALSE_LITERAL: + return parse_meta_item_lit (); + + case SUPER: + case SELF: + case CRATE: + case DOLLAR_SIGN: + case SCOPE_RESOLUTION: + return parse_path_meta_item (); + + default: + rust_error_at (peek_token ()->get_locus (), + "unrecognised token '%s' in meta item", + get_token_description (peek_token ()->get_id ())); + return nullptr; + } + } + + // else, check for path + if (peek_token (1)->get_id () == SCOPE_RESOLUTION) + { + // path + return parse_path_meta_item (); + } + + auto ident = peek_token ()->as_string (); + auto ident_locus = peek_token ()->get_locus (); + + if (is_end_meta_item_tok (peek_token (1)->get_id ())) + { + // meta word syntax + skip_token (); + return std::unique_ptr (new MetaWord (ident, ident_locus)); + } + + if (peek_token (1)->get_id () == EQUAL) + { + // maybe meta name value str syntax - check next 2 tokens + if (peek_token (2)->get_id () == STRING_LITERAL + && is_end_meta_item_tok (peek_token (3)->get_id ())) + { + // meta name value str syntax + auto &value_tok = peek_token (2); + auto value = value_tok->as_string (); + auto locus = value_tok->get_locus (); + + skip_token (2); + + // remove the quotes from the string value + std::string raw_value = unquote_string (std::move (value)); + + return std::unique_ptr ( + new MetaNameValueStr (ident, ident_locus, std::move (raw_value), + locus)); + } + else + { + // just interpret as path-based meta item + return parse_path_meta_item (); + } + } + + if (peek_token (1)->get_id () != LEFT_PAREN) + { + rust_error_at (peek_token (1)->get_locus (), + "unexpected token '%s' after identifier in attribute", + get_token_description (peek_token (1)->get_id ())); + return nullptr; + } + + // is it one of those special cases like not? + if (peek_token ()->get_id () == IDENTIFIER) + { + return parse_path_meta_item (); + } + + auto meta_items = parse_meta_item_seq (); + + // pass for meta name value str + std::vector meta_name_value_str_items; + for (const auto &item : meta_items) + { + std::unique_ptr converted_item + = item->to_meta_name_value_str (); + if (converted_item == nullptr) + { + meta_name_value_str_items.clear (); + break; + } + meta_name_value_str_items.push_back (std::move (*converted_item)); + } + // if valid, return this + if (!meta_name_value_str_items.empty ()) + { + return std::unique_ptr ( + new MetaListNameValueStr (ident, ident_locus, + std::move (meta_name_value_str_items))); + } + + // // pass for meta list idents + // std::vector ident_items; + // for (const auto &item : meta_items) + // { + // std::unique_ptr converted_ident (item->to_ident_item ()); + // if (converted_ident == nullptr) + // { + // ident_items.clear (); + // break; + // } + // ident_items.push_back (std::move (*converted_ident)); + // } + // // if valid return this + // if (!ident_items.empty ()) + // { + // return std::unique_ptr ( + // new MetaListIdents (std::move (ident), std::move (ident_items))); + // } + // // as currently no meta list ident, currently no path. may change in future + + // pass for meta list paths + std::vector path_items; + for (const auto &item : meta_items) + { + SimplePath converted_path (item->to_path_item ()); + if (converted_path.is_empty ()) + { + path_items.clear (); + break; + } + path_items.push_back (std::move (converted_path)); + } + if (!path_items.empty ()) + { + return std::unique_ptr ( + new MetaListPaths (ident, ident_locus, std::move (path_items))); + } + + rust_error_at (Linemap::unknown_location (), + "failed to parse any meta item inner"); + return nullptr; +} + +bool +AttributeParser::is_end_meta_item_tok (TokenId id) const +{ + return id == COMMA || id == RIGHT_PAREN; +} + +std::unique_ptr +AttributeParser::parse_path_meta_item () +{ + SimplePath path = parse_simple_path (); + if (path.is_empty ()) + { + rust_error_at (peek_token ()->get_locus (), + "failed to parse simple path in attribute"); + return nullptr; + } + + switch (peek_token ()->get_id ()) + { + case LEFT_PAREN: { + std::vector> meta_items + = parse_meta_item_seq (); + + return std::unique_ptr ( + new MetaItemSeq (std::move (path), std::move (meta_items))); + } + case EQUAL: { + skip_token (); + + Location locus = peek_token ()->get_locus (); + Literal lit = parse_literal (); + if (lit.is_error ()) + { + rust_error_at (peek_token ()->get_locus (), + "failed to parse literal in attribute"); + return nullptr; + } + LiteralExpr expr (std::move (lit), {}, locus); + // stream_pos++; + /* shouldn't be required anymore due to parsing literal actually + * skipping the token */ + return std::unique_ptr ( + new MetaItemPathLit (std::move (path), std::move (expr))); + } + case COMMA: + // just simple path + return std::unique_ptr ( + new MetaItemPath (std::move (path))); + default: + rust_error_at (peek_token ()->get_locus (), + "unrecognised token '%s' in meta item", + get_token_description (peek_token ()->get_id ())); + return nullptr; + } +} + +/* Parses a parenthesised sequence of meta item inners. Parentheses are + * required here. */ +std::vector> +AttributeParser::parse_meta_item_seq () +{ + int vec_length = token_stream.size (); + std::vector> meta_items; + + if (peek_token ()->get_id () != LEFT_PAREN) + { + rust_error_at (peek_token ()->get_locus (), + "missing left paren in delim token tree"); + return {}; + } + skip_token (); + + while (stream_pos < vec_length && peek_token ()->get_id () != RIGHT_PAREN) + { + std::unique_ptr inner = parse_meta_item_inner (); + if (inner == nullptr) + { + rust_error_at (peek_token ()->get_locus (), + "failed to parse inner meta item in attribute"); + return {}; + } + meta_items.push_back (std::move (inner)); + + if (peek_token ()->get_id () != COMMA) + break; + + skip_token (); + } + + if (peek_token ()->get_id () != RIGHT_PAREN) + { + rust_error_at (peek_token ()->get_locus (), + "missing right paren in delim token tree"); + return {}; + } + skip_token (); + + return meta_items; +} + +/* Collects any nested token trees into a flat token stream, suitable for + * parsing. */ +std::vector> +DelimTokenTree::to_token_stream () const +{ + std::vector> tokens; + for (const auto &tree : token_trees) + { + std::vector> stream = tree->to_token_stream (); + + tokens.insert (tokens.end (), std::make_move_iterator (stream.begin ()), + std::make_move_iterator (stream.end ())); + } + + tokens.shrink_to_fit (); + return tokens; +} + +Literal +AttributeParser::parse_literal () +{ + const std::unique_ptr &tok = peek_token (); + switch (tok->get_id ()) + { + case CHAR_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::CHAR, tok->get_type_hint ()); + case STRING_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::STRING, + tok->get_type_hint ()); + case BYTE_CHAR_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::BYTE, tok->get_type_hint ()); + case BYTE_STRING_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::BYTE_STRING, + tok->get_type_hint ()); + case INT_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::INT, tok->get_type_hint ()); + case FLOAT_LITERAL: + skip_token (); + return Literal (tok->as_string (), Literal::FLOAT, tok->get_type_hint ()); + case TRUE_LITERAL: + skip_token (); + return Literal ("true", Literal::BOOL, tok->get_type_hint ()); + case FALSE_LITERAL: + skip_token (); + return Literal ("false", Literal::BOOL, tok->get_type_hint ()); + default: + rust_error_at (tok->get_locus (), "expected literal - found '%s'", + get_token_description (tok->get_id ())); + return Literal::create_error (); + } +} + +SimplePath +AttributeParser::parse_simple_path () +{ + bool has_opening_scope_res = false; + if (peek_token ()->get_id () == SCOPE_RESOLUTION) + { + has_opening_scope_res = true; + skip_token (); + } + + std::vector segments; + + SimplePathSegment segment = parse_simple_path_segment (); + if (segment.is_error ()) + { + rust_error_at ( + peek_token ()->get_locus (), + "failed to parse simple path segment in attribute simple path"); + return SimplePath::create_empty (); + } + segments.push_back (std::move (segment)); + + while (peek_token ()->get_id () == SCOPE_RESOLUTION) + { + skip_token (); + + SimplePathSegment segment = parse_simple_path_segment (); + if (segment.is_error ()) + { + rust_error_at ( + peek_token ()->get_locus (), + "failed to parse simple path segment in attribute simple path"); + return SimplePath::create_empty (); + } + segments.push_back (std::move (segment)); + } + segments.shrink_to_fit (); + + return SimplePath (std::move (segments), has_opening_scope_res); +} + +SimplePathSegment +AttributeParser::parse_simple_path_segment () +{ + const std::unique_ptr &tok = peek_token (); + switch (tok->get_id ()) + { + case IDENTIFIER: + skip_token (); + return SimplePathSegment (tok->as_string (), tok->get_locus ()); + case SUPER: + skip_token (); + return SimplePathSegment ("super", tok->get_locus ()); + case SELF: + skip_token (); + return SimplePathSegment ("self", tok->get_locus ()); + case CRATE: + skip_token (); + return SimplePathSegment ("crate", tok->get_locus ()); + case DOLLAR_SIGN: + if (peek_token (1)->get_id () == CRATE) + { + skip_token (1); + return SimplePathSegment ("$crate", tok->get_locus ()); + } + gcc_fallthrough (); + default: + rust_error_at (tok->get_locus (), + "unexpected token '%s' in simple path segment", + get_token_description (tok->get_id ())); + return SimplePathSegment::create_error (); + } +} + +std::unique_ptr +AttributeParser::parse_meta_item_lit () +{ + Location locus = peek_token ()->get_locus (); + LiteralExpr lit_expr (parse_literal (), {}, locus); + return std::unique_ptr ( + new MetaItemLitExpr (std::move (lit_expr))); +} + +bool +AttrInputMetaItemContainer::check_cfg_predicate (const Session &session) const +{ + if (items.empty ()) + return false; + + for (const auto &inner_item : items) + { + if (!inner_item->check_cfg_predicate (session)) + return false; + } + + return true; +} + +bool +MetaItemLitExpr::check_cfg_predicate (const Session &) const +{ + /* as far as I can tell, a literal expr can never be a valid cfg body, so + * false */ + return false; +} + +bool +MetaListNameValueStr::check_cfg_predicate (const Session &session) const +{ + if (ident == "all") + { + for (const auto &str : strs) + { + if (!str.check_cfg_predicate (session)) + return false; + } + return true; + } + else if (ident == "any") + { + for (const auto &str : strs) + { + if (str.check_cfg_predicate (session)) + return true; + } + return false; + } + else if (ident == "not") + { + if (strs.size () != 1) + { + /* HACK: convert vector platform-dependent size_type to string to + * use in printf */ + rust_error_at (Linemap::unknown_location (), + "cfg predicate could not be checked for " + "MetaListNameValueStr with ident of " + "'not' because there are '%s' elements, not '1'", + std::to_string (strs.size ()).c_str ()); + return false; + } + + return !strs[0].check_cfg_predicate (session); + } + else + { + rust_error_at (Linemap::unknown_location (), + "cfg predicate could not be checked for " + "MetaListNameValueStr with ident of " + "'%s' - ident must be 'all' or 'any'", + ident.c_str ()); + return false; + } +} + +bool +MetaListPaths::check_cfg_predicate (const Session &session) const +{ + if (ident == "all") + { + for (const auto &path : paths) + { + if (!check_path_exists_in_cfg (session, path)) + return false; + } + return true; + } + else if (ident == "any") + { + for (const auto &path : paths) + { + if (check_path_exists_in_cfg (session, path)) + return true; + } + return false; + } + else if (ident == "not") + { + if (paths.size () != 1) + { + // HACK: convert vector platform-dependent size_type to string to + // use in printf + rust_error_at (Linemap::unknown_location (), + "cfg predicate could not be checked for MetaListPaths " + "with ident of 'not' " + "because there are '%s' elements, not '1'", + std::to_string (paths.size ()).c_str ()); + return false; + } + + return !check_path_exists_in_cfg (session, paths[0]); + } + else + { + rust_error_at (Linemap::unknown_location (), + "cfg predicate could not be checked for " + "MetaListNameValueStr with ident of " + "'%s' - ident must be 'all' or 'any'", + ident.c_str ()); + return false; + } +} + +bool +MetaListPaths::check_path_exists_in_cfg (const Session &session, + const SimplePath &path) const +{ + return session.options.target_data.has_key (path.as_string ()); +} + +bool +MetaItemSeq::check_cfg_predicate (const Session &session) const +{ + if (path.as_string () == "all") + { + for (const auto &item : seq) + { + if (!item->check_cfg_predicate (session)) + return false; + } + return true; + } + else if (path.as_string () == "any") + { + for (const auto &item : seq) + { + if (item->check_cfg_predicate (session)) + return true; + } + return false; + } + else if (path.as_string () == "not") + { + if (seq.size () != 1) + { + /* HACK: convert vector platform-dependent size_type to string to + * use in printf */ + rust_error_at (Linemap::unknown_location (), + "cfg predicate could not be checked for MetaItemSeq " + "with ident of 'not' " + "because there are '%s' elements, not '1'", + std::to_string (seq.size ()).c_str ()); + return false; + } + + return !seq[0]->check_cfg_predicate (session); + } + else + { + rust_error_at ( + Linemap::unknown_location (), + "cfg predicate could not be checked for MetaItemSeq with path of " + "'%s' - path must be 'all' or 'any'", + path.as_string ().c_str ()); + return false; + } +} + +bool +MetaWord::check_cfg_predicate (const Session &session) const +{ + return session.options.target_data.has_key (ident); +} + +bool +MetaItemPath::check_cfg_predicate (const Session &session) const +{ + /* Strictly speaking, this should always be false, but maybe do check + * relating to SimplePath being identifier. Currently, it would return true + * if path as identifier existed, and if the path in string form existed + * (though this shouldn't occur). */ + return session.options.target_data.has_key (path.as_string ()); +} + +bool +MetaNameValueStr::check_cfg_predicate (const Session &session) const +{ + // DEBUG + rust_debug ( + "checked key-value pair for cfg: '%s', '%s' - is%s in target data", + ident.c_str (), str.c_str (), + session.options.target_data.has_key_value_pair (ident, str) ? "" : " not"); + + return session.options.target_data.has_key_value_pair (ident, str); +} + +bool +MetaItemPathLit::check_cfg_predicate (const Session &session) const +{ + return session.options.target_data.has_key_value_pair (path.as_string (), + lit.as_string ()); +} + +std::vector> +Token::to_token_stream () const +{ + /* initialisation list doesn't work as it needs copy constructor, so have to + * do this */ + std::vector> dummy_vector; + dummy_vector.reserve (1); + dummy_vector.push_back (std::unique_ptr (clone_token_impl ())); + return dummy_vector; +} + +Attribute +MetaNameValueStr::to_attribute () const +{ + LiteralExpr lit_expr (str, Literal::LitType::STRING, + PrimitiveCoreType::CORETYPE_UNKNOWN, {}, str_locus); + // FIXME: What location do we put here? Is the literal above supposed to have + // an empty location as well? + // Should MetaNameValueStr keep a location? + return Attribute (SimplePath::from_str (ident, ident_locus), + std::unique_ptr ( + new AttrInputLiteral (std::move (lit_expr)))); +} + +Attribute +MetaItemPath::to_attribute () const +{ + return Attribute (path, nullptr); +} + +Attribute +MetaItemSeq::to_attribute () const +{ + std::vector> new_seq; + new_seq.reserve (seq.size ()); + for (const auto &e : seq) + new_seq.push_back (e->clone_meta_item_inner ()); + + std::unique_ptr new_seq_container ( + new AttrInputMetaItemContainer (std::move (new_seq))); + return Attribute (path, std::move (new_seq_container)); +} + +Attribute +MetaWord::to_attribute () const +{ + return Attribute (SimplePath::from_str (ident, ident_locus), nullptr); +} + +Attribute +MetaListPaths::to_attribute () const +{ + /* probably one of the most annoying conversions - have to lose specificity by + * turning it into just AttrInputMetaItemContainer (i.e. paths-only nature is + * no longer known). If conversions back are required, might have to do a + * "check all are paths" pass or something. */ + + std::vector> new_seq; + new_seq.reserve (paths.size ()); + for (const auto &e : paths) + new_seq.push_back (std::unique_ptr (new MetaItemPath (e))); + + std::unique_ptr new_seq_container ( + new AttrInputMetaItemContainer (std::move (new_seq))); + return Attribute (SimplePath::from_str (ident, ident_locus), + std::move (new_seq_container)); +} + +Attribute +MetaListNameValueStr::to_attribute () const +{ + std::vector> new_seq; + new_seq.reserve (strs.size ()); + for (const auto &e : strs) + new_seq.push_back ( + std::unique_ptr (new MetaNameValueStr (e))); + + std::unique_ptr new_seq_container ( + new AttrInputMetaItemContainer (std::move (new_seq))); + return Attribute (SimplePath::from_str (ident, ident_locus), + std::move (new_seq_container)); +} + +Attribute +MetaItemPathLit::to_attribute () const +{ + return Attribute (path, std::unique_ptr ( + new AttrInputLiteral (lit))); +} + +std::vector +AttrInputMetaItemContainer::separate_cfg_attrs () const +{ + rust_assert (!items.empty ()); + + if (items.size () == 1) + return {}; + + std::vector attrs; + attrs.reserve (items.size () - 1); + + for (auto it = items.begin () + 1; it != items.end (); ++it) + { + Attribute attr = (*it)->to_attribute (); + if (attr.is_empty ()) + { + /* TODO should this be an error that causes us to chuck out + * everything? */ + continue; + } + attrs.push_back (std::move (attr)); + } + + attrs.shrink_to_fit (); + return attrs; +} + +bool +Attribute::check_cfg_predicate (const Session &session) const +{ + /* assume that cfg predicate actually can exist, i.e. attribute has cfg or + * cfg_attr path */ + if (!has_attr_input () + || (path.as_string () != "cfg" && path.as_string () != "cfg_attr")) + { + // DEBUG message + rust_debug ( + "tried to check cfg predicate on attr that either has no input " + "or invalid path. attr: '%s'", + as_string ().c_str ()); + + return false; + } + + // assume that it has already been parsed + if (!is_parsed_to_meta_item ()) + return false; + + return attr_input->check_cfg_predicate (session); +} + +std::vector +Attribute::separate_cfg_attrs () const +{ + if (!has_attr_input () || path.as_string () != "cfg_attr") + return {}; + + // assume that it has already been parsed + if (!is_parsed_to_meta_item ()) + return {}; + + return attr_input->separate_cfg_attrs (); +} + +bool +Attribute::is_parsed_to_meta_item () const +{ + return has_attr_input () && attr_input->is_meta_item (); +} + +/* Visitor implementations - these are short but inlining can't happen anyway + * due to virtual functions and I didn't want to make the ast header includes + * any longer than they already are. */ + +void +Token::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +DelimTokenTree::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IdentifierExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Lifetime::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LifetimeParam::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ConstGenericParam::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +PathInExpression::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypePathSegment::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypePathSegmentGeneric::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypePathSegmentFunction::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypePath::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +QualifiedPathInExpression::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +QualifiedPathInType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LiteralExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +AttrInputLiteral::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaItemLitExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaItemPathLit::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +BorrowExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +DereferenceExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ErrorPropagationExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +NegationExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArithmeticOrLogicalExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ComparisonExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LazyBooleanExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypeCastExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +AssignmentExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +CompoundAssignmentExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +GroupedExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayElemsValues::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayElemsCopied::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayIndexExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleIndexExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprStruct::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprFieldIdentifier::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprFieldIdentifierValue::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprFieldIndexValue::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprStructFields::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructExprStructBase::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +CallExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MethodCallExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +FieldAccessExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ClosureExprInner::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +BlockExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ClosureExprInnerTyped::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ContinueExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +BreakExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeFromToExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeFromExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeToExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeFullExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeFromToInclExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangeToInclExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ReturnExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +UnsafeBlockExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LoopExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +WhileLoopExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +WhileLetLoopExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ForLoopExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfExprConseqElse::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfExprConseqIf::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfExprConseqIfLet::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfLetExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfLetExprConseqElse::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfLetExprConseqIf::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IfLetExprConseqIfLet::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MatchExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +AwaitExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +AsyncBlockExpr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypeParam::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LifetimeWhereClauseItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypeBoundWhereClauseItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Method::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Module::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExternCrate::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +UseTreeGlob::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +UseTreeList::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +UseTreeRebind::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +UseDeclaration::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Function::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TypeAlias::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructStruct::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleStruct::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +EnumItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +EnumItemTuple::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +EnumItemStruct::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +EnumItemDiscriminant::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Enum::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Union::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ConstantItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StaticItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitItemFunc::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitItemMethod::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitItemConst::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitItemType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +Trait::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +InherentImpl::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitImpl::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExternalStaticItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExternalFunctionItem::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExternBlock::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MacroMatchFragment::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MacroMatchRepetition::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MacroMatcher::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MacroRulesDefinition::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MacroInvocation::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LiteralPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +IdentifierPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +WildcardPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangePatternBoundLiteral::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangePatternBoundPath::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangePatternBoundQualPath::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RangePattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ReferencePattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructPatternFieldTuplePat::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructPatternFieldIdentPat::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructPatternFieldIdent::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +StructPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleStructItemsNoRange::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleStructItemsRange::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleStructPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TuplePatternItemsMultiple::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TuplePatternItemsRanged::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TuplePattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +GroupedPattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +SlicePattern::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +EmptyStmt::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +LetStmt::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExprStmtWithoutBlock::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ExprStmtWithBlock::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitBound::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ImplTraitType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitObjectType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ParenthesisedType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ImplTraitTypeOneBound::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TraitObjectTypeOneBound::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +NeverType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +RawPointerType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ReferenceType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +SliceType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +InferredType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +BareFunctionType::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaItemSeq::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaItemPath::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaListPaths::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaNameValueStr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaListNameValueStr::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +AttrInputMetaItemContainer::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +void +MetaWord::accept_vis (ASTVisitor &vis) +{ + vis.visit (*this); +} + +GenericArg +GenericArg::disambiguate_to_const () const +{ + rust_assert (get_kind () == Kind::Either); + + // FIXME: is it fine to have no outer attributes? + return GenericArg::create_const ( + std::unique_ptr (new IdentifierExpr (path, {}, locus))); +} + +GenericArg +GenericArg::disambiguate_to_type () const +{ + rust_assert (get_kind () == Kind::Either); + + auto segment = std::unique_ptr ( + new TypePathSegment (path, false, locus)); + auto segments = std::vector> (); + segments.emplace_back (std::move (segment)); + + return GenericArg::create_type ( + std::unique_ptr (new TypePath (std::move (segments), locus))); +} + +} // namespace AST +} // namespace Rust diff --git a/gcc/rust/ast/rust-ast-full.h b/gcc/rust/ast/rust-ast-full.h new file mode 100644 index 00000000000..5ab136c61b6 --- /dev/null +++ b/gcc/rust/ast/rust-ast-full.h @@ -0,0 +1,31 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_FULL_H +#define RUST_AST_FULL_H +// Use as a fast way of including all aspects of the AST (i.e. all headers) +#include "rust-ast.h" +#include "rust-expr.h" +#include "rust-item.h" +#include "rust-path.h" +#include "rust-pattern.h" +#include "rust-stmt.h" +#include "rust-type.h" +#include "rust-macro.h" + +#endif diff --git a/gcc/rust/ast/rust-ast-visitor.h b/gcc/rust/ast/rust-ast-visitor.h new file mode 100644 index 00000000000..bbb04771fea --- /dev/null +++ b/gcc/rust/ast/rust-ast-visitor.h @@ -0,0 +1,234 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_VISITOR_H +#define RUST_AST_VISITOR_H +// Visitor base for AST + +// full include not required - only forward decls +#include "rust-ast-full-decls.h" + +namespace Rust { +namespace AST { +/* Pure abstract class that provides an interface for accessing different + * classes of the AST. */ +class ASTVisitor +{ +public: + // only concrete class overloads are required + + // rust-ast.h + // virtual void visit(AttrInput& attr_input) = 0; + // virtual void visit(TokenTree& token_tree) = 0; + // virtual void visit(MacroMatch& macro_match) = 0; + virtual void visit (Token &tok) = 0; + virtual void visit (DelimTokenTree &delim_tok_tree) = 0; + virtual void visit (AttrInputMetaItemContainer &input) = 0; + // virtual void visit(MetaItem& meta_item) = 0; + // virtual void visit(Stmt& stmt) = 0; + // virtual void visit(Expr& expr) = 0; + virtual void visit (IdentifierExpr &ident_expr) = 0; + // virtual void visit(Pattern& pattern) = 0; + // virtual void visit(Type& type) = 0; + // virtual void visit(TypeParamBound& type_param_bound) = 0; + virtual void visit (Lifetime &lifetime) = 0; + // virtual void visit(GenericParam& generic_param) = 0; + virtual void visit (LifetimeParam &lifetime_param) = 0; + virtual void visit (ConstGenericParam &const_param) = 0; + // virtual void visit(TraitItem& trait_item) = 0; + // virtual void visit(InherentImplItem& inherent_impl_item) = 0; + // virtual void visit(TraitImplItem& trait_impl_item) = 0; + + // rust-path.h + virtual void visit (PathInExpression &path) = 0; + virtual void visit (TypePathSegment &segment) = 0; + virtual void visit (TypePathSegmentGeneric &segment) = 0; + virtual void visit (TypePathSegmentFunction &segment) = 0; + virtual void visit (TypePath &path) = 0; + virtual void visit (QualifiedPathInExpression &path) = 0; + virtual void visit (QualifiedPathInType &path) = 0; + + // rust-expr.h + virtual void visit (LiteralExpr &expr) = 0; + virtual void visit (AttrInputLiteral &attr_input) = 0; + virtual void visit (MetaItemLitExpr &meta_item) = 0; + virtual void visit (MetaItemPathLit &meta_item) = 0; + virtual void visit (BorrowExpr &expr) = 0; + virtual void visit (DereferenceExpr &expr) = 0; + virtual void visit (ErrorPropagationExpr &expr) = 0; + virtual void visit (NegationExpr &expr) = 0; + virtual void visit (ArithmeticOrLogicalExpr &expr) = 0; + virtual void visit (ComparisonExpr &expr) = 0; + virtual void visit (LazyBooleanExpr &expr) = 0; + virtual void visit (TypeCastExpr &expr) = 0; + virtual void visit (AssignmentExpr &expr) = 0; + virtual void visit (CompoundAssignmentExpr &expr) = 0; + virtual void visit (GroupedExpr &expr) = 0; + // virtual void visit(ArrayElems& elems) = 0; + virtual void visit (ArrayElemsValues &elems) = 0; + virtual void visit (ArrayElemsCopied &elems) = 0; + virtual void visit (ArrayExpr &expr) = 0; + virtual void visit (ArrayIndexExpr &expr) = 0; + virtual void visit (TupleExpr &expr) = 0; + virtual void visit (TupleIndexExpr &expr) = 0; + virtual void visit (StructExprStruct &expr) = 0; + // virtual void visit(StructExprField& field) = 0; + virtual void visit (StructExprFieldIdentifier &field) = 0; + virtual void visit (StructExprFieldIdentifierValue &field) = 0; + virtual void visit (StructExprFieldIndexValue &field) = 0; + virtual void visit (StructExprStructFields &expr) = 0; + virtual void visit (StructExprStructBase &expr) = 0; + virtual void visit (CallExpr &expr) = 0; + virtual void visit (MethodCallExpr &expr) = 0; + virtual void visit (FieldAccessExpr &expr) = 0; + virtual void visit (ClosureExprInner &expr) = 0; + virtual void visit (BlockExpr &expr) = 0; + virtual void visit (ClosureExprInnerTyped &expr) = 0; + virtual void visit (ContinueExpr &expr) = 0; + virtual void visit (BreakExpr &expr) = 0; + virtual void visit (RangeFromToExpr &expr) = 0; + virtual void visit (RangeFromExpr &expr) = 0; + virtual void visit (RangeToExpr &expr) = 0; + virtual void visit (RangeFullExpr &expr) = 0; + virtual void visit (RangeFromToInclExpr &expr) = 0; + virtual void visit (RangeToInclExpr &expr) = 0; + virtual void visit (ReturnExpr &expr) = 0; + virtual void visit (UnsafeBlockExpr &expr) = 0; + virtual void visit (LoopExpr &expr) = 0; + virtual void visit (WhileLoopExpr &expr) = 0; + virtual void visit (WhileLetLoopExpr &expr) = 0; + virtual void visit (ForLoopExpr &expr) = 0; + virtual void visit (IfExpr &expr) = 0; + virtual void visit (IfExprConseqElse &expr) = 0; + virtual void visit (IfExprConseqIf &expr) = 0; + virtual void visit (IfExprConseqIfLet &expr) = 0; + virtual void visit (IfLetExpr &expr) = 0; + virtual void visit (IfLetExprConseqElse &expr) = 0; + virtual void visit (IfLetExprConseqIf &expr) = 0; + virtual void visit (IfLetExprConseqIfLet &expr) = 0; + // virtual void visit(MatchCase& match_case) = 0; + // virtual void visit (MatchCaseBlockExpr &match_case) = 0; + // virtual void visit (MatchCaseExpr &match_case) = 0; + virtual void visit (MatchExpr &expr) = 0; + virtual void visit (AwaitExpr &expr) = 0; + virtual void visit (AsyncBlockExpr &expr) = 0; + + // rust-item.h + virtual void visit (TypeParam ¶m) = 0; + // virtual void visit(WhereClauseItem& item) = 0; + virtual void visit (LifetimeWhereClauseItem &item) = 0; + virtual void visit (TypeBoundWhereClauseItem &item) = 0; + virtual void visit (Method &method) = 0; + virtual void visit (Module &module) = 0; + virtual void visit (ExternCrate &crate) = 0; + // virtual void visit(UseTree& use_tree) = 0; + virtual void visit (UseTreeGlob &use_tree) = 0; + virtual void visit (UseTreeList &use_tree) = 0; + virtual void visit (UseTreeRebind &use_tree) = 0; + virtual void visit (UseDeclaration &use_decl) = 0; + virtual void visit (Function &function) = 0; + virtual void visit (TypeAlias &type_alias) = 0; + virtual void visit (StructStruct &struct_item) = 0; + virtual void visit (TupleStruct &tuple_struct) = 0; + virtual void visit (EnumItem &item) = 0; + virtual void visit (EnumItemTuple &item) = 0; + virtual void visit (EnumItemStruct &item) = 0; + virtual void visit (EnumItemDiscriminant &item) = 0; + virtual void visit (Enum &enum_item) = 0; + virtual void visit (Union &union_item) = 0; + virtual void visit (ConstantItem &const_item) = 0; + virtual void visit (StaticItem &static_item) = 0; + virtual void visit (TraitItemFunc &item) = 0; + virtual void visit (TraitItemMethod &item) = 0; + virtual void visit (TraitItemConst &item) = 0; + virtual void visit (TraitItemType &item) = 0; + virtual void visit (Trait &trait) = 0; + virtual void visit (InherentImpl &impl) = 0; + virtual void visit (TraitImpl &impl) = 0; + // virtual void visit(ExternalItem& item) = 0; + virtual void visit (ExternalStaticItem &item) = 0; + virtual void visit (ExternalFunctionItem &item) = 0; + virtual void visit (ExternBlock &block) = 0; + + // rust-macro.h + virtual void visit (MacroMatchFragment &match) = 0; + virtual void visit (MacroMatchRepetition &match) = 0; + virtual void visit (MacroMatcher &matcher) = 0; + virtual void visit (MacroRulesDefinition &rules_def) = 0; + virtual void visit (MacroInvocation ¯o_invoc) = 0; + virtual void visit (MetaItemPath &meta_item) = 0; + virtual void visit (MetaItemSeq &meta_item) = 0; + virtual void visit (MetaWord &meta_item) = 0; + virtual void visit (MetaNameValueStr &meta_item) = 0; + virtual void visit (MetaListPaths &meta_item) = 0; + virtual void visit (MetaListNameValueStr &meta_item) = 0; + + // rust-pattern.h + virtual void visit (LiteralPattern &pattern) = 0; + virtual void visit (IdentifierPattern &pattern) = 0; + virtual void visit (WildcardPattern &pattern) = 0; + // virtual void visit(RangePatternBound& bound) = 0; + virtual void visit (RangePatternBoundLiteral &bound) = 0; + virtual void visit (RangePatternBoundPath &bound) = 0; + virtual void visit (RangePatternBoundQualPath &bound) = 0; + virtual void visit (RangePattern &pattern) = 0; + virtual void visit (ReferencePattern &pattern) = 0; + // virtual void visit(StructPatternField& field) = 0; + virtual void visit (StructPatternFieldTuplePat &field) = 0; + virtual void visit (StructPatternFieldIdentPat &field) = 0; + virtual void visit (StructPatternFieldIdent &field) = 0; + virtual void visit (StructPattern &pattern) = 0; + // virtual void visit(TupleStructItems& tuple_items) = 0; + virtual void visit (TupleStructItemsNoRange &tuple_items) = 0; + virtual void visit (TupleStructItemsRange &tuple_items) = 0; + virtual void visit (TupleStructPattern &pattern) = 0; + // virtual void visit(TuplePatternItems& tuple_items) = 0; + virtual void visit (TuplePatternItemsMultiple &tuple_items) = 0; + virtual void visit (TuplePatternItemsRanged &tuple_items) = 0; + virtual void visit (TuplePattern &pattern) = 0; + virtual void visit (GroupedPattern &pattern) = 0; + virtual void visit (SlicePattern &pattern) = 0; + + // rust-stmt.h + virtual void visit (EmptyStmt &stmt) = 0; + virtual void visit (LetStmt &stmt) = 0; + virtual void visit (ExprStmtWithoutBlock &stmt) = 0; + virtual void visit (ExprStmtWithBlock &stmt) = 0; + + // rust-type.h + virtual void visit (TraitBound &bound) = 0; + virtual void visit (ImplTraitType &type) = 0; + virtual void visit (TraitObjectType &type) = 0; + virtual void visit (ParenthesisedType &type) = 0; + virtual void visit (ImplTraitTypeOneBound &type) = 0; + virtual void visit (TraitObjectTypeOneBound &type) = 0; + virtual void visit (TupleType &type) = 0; + virtual void visit (NeverType &type) = 0; + virtual void visit (RawPointerType &type) = 0; + virtual void visit (ReferenceType &type) = 0; + virtual void visit (ArrayType &type) = 0; + virtual void visit (SliceType &type) = 0; + virtual void visit (InferredType &type) = 0; + virtual void visit (BareFunctionType &type) = 0; + + // TODO: rust-cond-compilation.h visiting? not currently used +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h new file mode 100644 index 00000000000..461a2460f8f --- /dev/null +++ b/gcc/rust/ast/rust-ast.h @@ -0,0 +1,2007 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_BASE_H +#define RUST_AST_BASE_H +// Base for AST used in gccrs, basically required by all specific ast things + +#include "rust-system.h" +#include "rust-hir-map.h" +#include "rust-token.h" +#include "rust-location.h" + +namespace Rust { +// TODO: remove typedefs and make actual types for these +typedef std::string Identifier; +typedef int TupleIndex; +struct Session; + +namespace AST { +// foward decl: ast visitor +class ASTVisitor; +using AttrVec = std::vector; + +// The available kinds of AST Nodes +enum Kind +{ + UNKNOWN, + MACRO_RULES_DEFINITION, + MACRO_INVOCATION, +}; + +// Abstract base class for all AST elements +class Node +{ +public: + /** + * Get the kind of Node this is. This is used to differentiate various AST + * elements with very little overhead when extracting the derived type through + * static casting is not necessary. + */ + // FIXME: Mark this as `= 0` in the future to make sure every node implements + // it + virtual Kind get_ast_kind () const { return Kind::UNKNOWN; } +}; + +// Delimiter types - used in macros and whatever. +enum DelimType +{ + PARENS, + SQUARE, + CURLY +}; + +// forward decl for use in token tree method +class Token; + +// A tree of tokens (or a single token) - abstract base class +class TokenTree +{ +public: + virtual ~TokenTree () {} + + // Unique pointer custom clone function + std::unique_ptr clone_token_tree () const + { + return std::unique_ptr (clone_token_tree_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + /* Converts token tree to a flat token stream. Tokens must be pointer to avoid + * mutual dependency with Token. */ + virtual std::vector > to_token_stream () const = 0; + +protected: + // pure virtual clone implementation + virtual TokenTree *clone_token_tree_impl () const = 0; +}; + +// Abstract base class for a macro match +class MacroMatch +{ +public: + enum MacroMatchType + { + Fragment, + Repetition, + Matcher, + Tok + }; + + virtual ~MacroMatch () {} + + virtual std::string as_string () const = 0; + virtual Location get_match_locus () const = 0; + + // Unique pointer custom clone function + std::unique_ptr clone_macro_match () const + { + return std::unique_ptr (clone_macro_match_impl ()); + } + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual MacroMatchType get_macro_match_type () const = 0; + +protected: + // pure virtual clone implementation + virtual MacroMatch *clone_macro_match_impl () const = 0; +}; + +// A token is a kind of token tree (except delimiter tokens) +class Token : public TokenTree, public MacroMatch +{ + // A token is a kind of token tree (except delimiter tokens) + // A token is a kind of MacroMatch (except $ and delimiter tokens) +#if 0 + // TODO: improve member variables - current ones are the same as lexer token + // Token kind. + TokenId token_id; + // Token location. + Location locus; + // Associated text (if any) of token. + std::string str; + // Token type hint (if any). + PrimitiveCoreType type_hint; +#endif + + const_TokenPtr tok_ref; + + /* new idea: wrapper around const_TokenPtr used for heterogeneuous storage in + * token trees. rather than convert back and forth when parsing macros, just + * wrap it. */ + +public: + // Unique pointer custom clone function + std::unique_ptr clone_token () const + { + return std::unique_ptr (clone_token_impl ()); + } + +#if 0 + /* constructor from general text - avoid using if lexer const_TokenPtr is + * available */ + Token (TokenId token_id, Location locus, std::string str, + PrimitiveCoreType type_hint) + : token_id (token_id), locus (locus), str (std::move (str)), + type_hint (type_hint) + {} +#endif + // not doable with new implementation - will have to make a const_TokenPtr + + // Constructor from lexer const_TokenPtr +#if 0 + /* TODO: find workaround for std::string being nullptr - probably have to + * introduce new method in lexer Token, or maybe make conversion method + * there */ + Token (const_TokenPtr lexer_token_ptr) + : token_id (lexer_token_ptr->get_id ()), + locus (lexer_token_ptr->get_locus ()), str (""), + type_hint (lexer_token_ptr->get_type_hint ()) + { + // FIXME: change to "should have str" later? + if (lexer_token_ptr->has_str ()) + { + str = lexer_token_ptr->get_str (); + + // DEBUG + rust_debug ("ast token created with str '%s'", str.c_str ()); + } + else + { + // FIXME: is this returning correct thing? + str = lexer_token_ptr->get_token_description (); + + // DEBUG + rust_debug ("ast token created with string '%s'", str.c_str ()); + } + + // DEBUG + if (lexer_token_ptr->should_have_str () && !lexer_token_ptr->has_str ()) + { + rust_debug ( + "BAD: for token '%s', should have string but does not!", + lexer_token_ptr->get_token_description ()); + } + } +#endif + Token (const_TokenPtr lexer_tok_ptr) : tok_ref (std::move (lexer_tok_ptr)) {} + + bool is_string_lit () const + { + switch (get_id ()) + { + case STRING_LITERAL: + case BYTE_STRING_LITERAL: + return true; + default: + return false; + } + } + + std::string as_string () const override; + Location get_match_locus () const override { return tok_ref->get_locus (); }; + + void accept_vis (ASTVisitor &vis) override; + + // Return copy of itself but in token stream form. + std::vector > to_token_stream () const override; + + TokenId get_id () const { return tok_ref->get_id (); } + const std::string &get_str () const { return tok_ref->get_str (); } + + Location get_locus () const { return tok_ref->get_locus (); } + + PrimitiveCoreType get_type_hint () const { return tok_ref->get_type_hint (); } + + // Get a new token pointer copy. + const_TokenPtr get_tok_ptr () const { return tok_ref; } + + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Tok; + } + +protected: + // No virtual for now as not polymorphic but can be in future + /*virtual*/ Token *clone_token_impl () const { return new Token (*this); } + + /* Use covariance to implement clone function as returning this object rather + * than base */ + Token *clone_token_tree_impl () const final override + { + return clone_token_impl (); + } + + /* Use covariance to implement clone function as returning this object rather + * than base */ + Token *clone_macro_match_impl () const final override + { + return clone_token_impl (); + } +}; + +// A literal - value with a type. Used in LiteralExpr and LiteralPattern. +struct Literal +{ +public: + enum LitType + { + CHAR, + STRING, + BYTE, + BYTE_STRING, + INT, + FLOAT, + BOOL, + ERROR + }; + +private: + /* TODO: maybe make subclasses of each type of literal with their typed values + * (or generics) */ + std::string value_as_string; + LitType type; + PrimitiveCoreType type_hint; + +public: + std::string as_string () const { return value_as_string; } + + LitType get_lit_type () const { return type; } + + PrimitiveCoreType get_type_hint () const { return type_hint; } + + Literal (std::string value_as_string, LitType type, + PrimitiveCoreType type_hint) + : value_as_string (std::move (value_as_string)), type (type), + type_hint (type_hint) + {} + + static Literal create_error () + { + return Literal ("", ERROR, PrimitiveCoreType::CORETYPE_UNKNOWN); + } + + // Returns whether literal is in an invalid state. + bool is_error () const { return type == ERROR; } +}; + +/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to + * be defined */ +class AttrInputLiteral; + +/* TODO: move applicable stuff into here or just don't include it because + * nothing uses it A segment of a path (maybe) */ +class PathSegment +{ +public: + virtual ~PathSegment () {} + + virtual std::string as_string () const = 0; + + // TODO: add visitor here? +}; + +// A segment of a simple path without generic or type arguments +class SimplePathSegment : public PathSegment +{ + std::string segment_name; + Location locus; + NodeId node_id; + + // only allow identifiers, "super", "self", "crate", or "$crate" +public: + // TODO: put checks in constructor to enforce this rule? + SimplePathSegment (std::string segment_name, Location locus) + : segment_name (std::move (segment_name)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + /* Returns whether simple path segment is in an invalid state (currently, if + * empty). */ + bool is_error () const { return segment_name.empty (); } + + // Creates an error SimplePathSegment + static SimplePathSegment create_error () + { + return SimplePathSegment (std::string (""), Location ()); + } + + std::string as_string () const override; + + Location get_locus () const { return locus; } + NodeId get_node_id () const { return node_id; } + const std::string &get_segment_name () const { return segment_name; } + bool is_super_path_seg () const + { + return as_string ().compare ("super") == 0; + } + bool is_crate_path_seg () const + { + return as_string ().compare ("crate") == 0; + } + bool is_lower_self () const { return as_string ().compare ("self") == 0; } + bool is_big_self () const { return as_string ().compare ("Self") == 0; } +}; + +// A simple path without generic or type arguments +class SimplePath +{ + bool has_opening_scope_resolution; + std::vector segments; + Location locus; + NodeId node_id; + +public: + // Constructor + SimplePath (std::vector path_segments, + bool has_opening_scope_resolution = false, + Location locus = Location ()) + : has_opening_scope_resolution (has_opening_scope_resolution), + segments (std::move (path_segments)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Creates an empty SimplePath. + static SimplePath create_empty () + { + return SimplePath (std::vector ()); + } + + // Returns whether the SimplePath is empty, i.e. has path segments. + bool is_empty () const { return segments.empty (); } + + std::string as_string () const; + + Location get_locus () const { return locus; } + NodeId get_node_id () const { return node_id; } + + // does this need visitor if not polymorphic? probably not + + // path-to-string comparison operator + bool operator== (const std::string &rhs) const + { + return !has_opening_scope_resolution && segments.size () == 1 + && segments[0].as_string () == rhs; + } + + /* Creates a single-segment SimplePath from a string. This will not check to + * ensure that this is a valid identifier in path, so be careful. Also, this + * will have no location data. + * TODO have checks? */ + static SimplePath from_str (std::string str, Location locus) + { + std::vector single_segments + = {AST::SimplePathSegment (std::move (str), locus)}; + return SimplePath (std::move (single_segments)); + } + + const std::vector &get_segments () const + { + return segments; + } + + std::vector &get_segments () { return segments; } +}; + +// path-to-string inverse comparison operator +inline bool +operator!= (const SimplePath &lhs, const std::string &rhs) +{ + return !(lhs == rhs); +} + +// forward decl for Attribute +class AttrInput; + +// aka Attr +// Attribute AST representation +struct Attribute +{ +private: + SimplePath path; + + // bool has_attr_input; + std::unique_ptr attr_input; + + Location locus; + + // TODO: maybe a variable storing whether attr input is parsed or not + +public: + // Returns whether Attribute has AttrInput + bool has_attr_input () const { return attr_input != nullptr; } + + // Constructor has pointer AttrInput for polymorphism reasons + Attribute (SimplePath path, std::unique_ptr input, + Location locus = Location ()) + : path (std::move (path)), attr_input (std::move (input)), locus (locus) + {} + + // default destructor + ~Attribute () = default; + + // no point in being defined inline as requires virtual call anyway + Attribute (const Attribute &other); + + // no point in being defined inline as requires virtual call anyway + Attribute &operator= (const Attribute &other); + + // default move semantics + Attribute (Attribute &&other) = default; + Attribute &operator= (Attribute &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_attribute () const + { + return std::unique_ptr (clone_attribute_impl ()); + } + + // Creates an empty attribute (which is invalid) + static Attribute create_empty () + { + return Attribute (SimplePath::create_empty (), nullptr); + } + + // Returns whether the attribute is considered an "empty" attribute. + bool is_empty () const { return attr_input == nullptr && path.is_empty (); } + + Location get_locus () const { return locus; } + + AttrInput &get_attr_input () const { return *attr_input; } + + /* e.g.: + #![crate_type = "lib"] + #[test] + #[cfg(target_os = "linux")] + #[allow(non_camel_case_types)] + #![allow(unused_variables)] + */ + + // Full built-in attribute list: + /* cfg + * cfg_attr + * test + * ignore + * should_panic + * derive + * macro_export + * macro_use + * proc_macro + * proc_macro_derive + * proc_macro_attribute + * allow + * warn + * deny + * forbid + * deprecated + * must_use + * link + * link_name + * no_link + * repr + * crate_type + * no_main + * export_name + * link_section + * no_mangle + * used + * crate_name + * inline + * cold + * no_builtins + * target_feature + * doc + * no_std + * no_implicit_prelude + * path + * recursion_limit + * type_length_limit + * panic_handler + * global_allocator + * windows_subsystem + * feature */ + + std::string as_string () const; + + // no visitor pattern as not currently polymorphic + + const SimplePath &get_path () const { return path; } + SimplePath &get_path () { return path; } + + // Call to parse attribute body to meta item syntax. + void parse_attr_to_meta_item (); + + /* Determines whether cfg predicate is true and item with attribute should not + * be stripped. Attribute body must already be parsed to meta item. */ + bool check_cfg_predicate (const Session &session) const; + + // Returns whether body has been parsed to meta item form or not. + bool is_parsed_to_meta_item () const; + + /* Returns any attributes generated from cfg_attr attributes. Attribute body + * must already be parsed to meta item. */ + std::vector separate_cfg_attrs () const; + +protected: + // not virtual as currently no subclasses of Attribute, but could be in future + /*virtual*/ Attribute *clone_attribute_impl () const + { + return new Attribute (*this); + } +}; + +// Attribute body - abstract base class +class AttrInput +{ +public: + enum AttrInputType + { + LITERAL, + META_ITEM, + TOKEN_TREE, + }; + + virtual ~AttrInput () {} + + // Unique pointer custom clone function + std::unique_ptr clone_attr_input () const + { + return std::unique_ptr (clone_attr_input_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual bool check_cfg_predicate (const Session &session) const = 0; + + // Parse attribute input to meta item, if possible + virtual AttrInput *parse_to_meta_item () const { return nullptr; } + + virtual std::vector separate_cfg_attrs () const { return {}; } + + // Returns whether attr input has been parsed to meta item syntax. + virtual bool is_meta_item () const = 0; + + virtual AttrInputType get_attr_input_type () const = 0; + +protected: + // pure virtual clone implementation + virtual AttrInput *clone_attr_input_impl () const = 0; +}; + +// Forward decl - defined in rust-macro.h +class MetaNameValueStr; + +// abstract base meta item inner class +class MetaItemInner +{ +protected: + // pure virtual as MetaItemInner + virtual MetaItemInner *clone_meta_item_inner_impl () const = 0; + +public: + // Unique pointer custom clone function + std::unique_ptr clone_meta_item_inner () const + { + return std::unique_ptr (clone_meta_item_inner_impl ()); + } + + virtual ~MetaItemInner (); + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + /* HACK: used to simplify parsing - creates a copy of that type, or returns + * null */ + virtual std::unique_ptr to_meta_name_value_str () const; + + // HACK: used to simplify parsing - same thing + virtual SimplePath to_path_item () const + { + return SimplePath::create_empty (); + } + + virtual Attribute to_attribute () const { return Attribute::create_empty (); } + + virtual bool check_cfg_predicate (const Session &session) const = 0; + + virtual bool is_key_value_pair () const { return false; } +}; + +// Container used to store MetaItems as AttrInput (bridge-ish kinda thing) +class AttrInputMetaItemContainer : public AttrInput +{ + std::vector > items; + +public: + AttrInputMetaItemContainer ( + std::vector > items) + : items (std::move (items)) + {} + + // copy constructor with vector clone + AttrInputMetaItemContainer (const AttrInputMetaItemContainer &other) + { + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_meta_item_inner ()); + } + + // copy assignment operator with vector clone + AttrInputMetaItemContainer & + operator= (const AttrInputMetaItemContainer &other) + { + AttrInput::operator= (other); + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_meta_item_inner ()); + + return *this; + } + + // default move constructors + AttrInputMetaItemContainer (AttrInputMetaItemContainer &&other) = default; + AttrInputMetaItemContainer &operator= (AttrInputMetaItemContainer &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + + AttrInputType get_attr_input_type () const final override + { + return AttrInput::AttrInputType::META_ITEM; + } + + // Clones this object. + std::unique_ptr + clone_attr_input_meta_item_container () const + { + return std::unique_ptr ( + clone_attr_input_meta_item_container_impl ()); + } + + std::vector separate_cfg_attrs () const override; + + bool is_meta_item () const override { return true; } + + // TODO: this mutable getter seems dodgy + std::vector > &get_items () { return items; } + const std::vector > &get_items () const + { + return items; + } + +protected: + // Use covariance to implement clone function as returning this type + AttrInputMetaItemContainer *clone_attr_input_impl () const final override + { + return clone_attr_input_meta_item_container_impl (); + } + + AttrInputMetaItemContainer *clone_attr_input_meta_item_container_impl () const + { + return new AttrInputMetaItemContainer (*this); + } +}; + +// A token tree with delimiters +class DelimTokenTree : public TokenTree, public AttrInput +{ + DelimType delim_type; + std::vector > token_trees; + Location locus; + +protected: + DelimTokenTree *clone_delim_tok_tree_impl () const + { + return new DelimTokenTree (*this); + } + + /* Use covariance to implement clone function as returning a DelimTokenTree + * object */ + DelimTokenTree *clone_attr_input_impl () const final override + { + return clone_delim_tok_tree_impl (); + } + + /* Use covariance to implement clone function as returning a DelimTokenTree + * object */ + DelimTokenTree *clone_token_tree_impl () const final override + { + return clone_delim_tok_tree_impl (); + } + +public: + DelimTokenTree (DelimType delim_type, + std::vector > token_trees + = std::vector > (), + Location locus = Location ()) + : delim_type (delim_type), token_trees (std::move (token_trees)), + locus (locus) + {} + + // Copy constructor with vector clone + DelimTokenTree (DelimTokenTree const &other) + : delim_type (other.delim_type), locus (other.locus) + { + token_trees.reserve (other.token_trees.size ()); + for (const auto &e : other.token_trees) + token_trees.push_back (e->clone_token_tree ()); + } + + // overloaded assignment operator with vector clone + DelimTokenTree &operator= (DelimTokenTree const &other) + { + delim_type = other.delim_type; + locus = other.locus; + + token_trees.reserve (other.token_trees.size ()); + for (const auto &e : other.token_trees) + token_trees.push_back (e->clone_token_tree ()); + + return *this; + } + + // move constructors + DelimTokenTree (DelimTokenTree &&other) = default; + DelimTokenTree &operator= (DelimTokenTree &&other) = default; + + static DelimTokenTree create_empty () { return DelimTokenTree (PARENS); } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &) const override + { + // this should never be called - should be converted first + rust_assert (false); + return false; + } + + AttrInputMetaItemContainer *parse_to_meta_item () const override; + + std::vector > to_token_stream () const override; + + std::unique_ptr clone_delim_token_tree () const + { + return std::unique_ptr (clone_delim_tok_tree_impl ()); + } + + bool is_meta_item () const override { return false; } + + AttrInputType get_attr_input_type () const final override + { + return AttrInput::AttrInputType::TOKEN_TREE; + } + + std::vector > &get_token_trees () + { + return token_trees; + } + + DelimType get_delim_type () const { return delim_type; } +}; + +/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to + * be defined */ +class AttrInputLiteral; + +// abstract base meta item class +class MetaItem : public MetaItemInner +{ +}; + +// Forward decl - defined in rust-expr.h +class MetaItemLitExpr; + +// Forward decl - defined in rust-expr.h +class MetaItemPathLit; + +// Forward decl - defined in rust-macro.h +class MetaItemPath; + +// Forward decl - defined in rust-macro.h +class MetaItemSeq; + +// Forward decl - defined in rust-macro.h +class MetaWord; + +// Forward decl - defined in rust-macro.h +class MetaListPaths; + +// Forward decl - defined in rust-macro.h +class MetaListNameValueStr; + +/* Base statement abstract class. Note that most "statements" are not allowed in + * top-level module scope - only a subclass of statements called "items" are. */ +class Stmt : public Node +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_stmt () const + { + return std::unique_ptr (clone_stmt_impl ()); + } + + virtual ~Stmt () {} + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual Location get_locus () const = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + NodeId get_node_id () const { return node_id; } + + virtual bool is_item () const = 0; + +protected: + Stmt () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + // Clone function implementation as pure virtual method + virtual Stmt *clone_stmt_impl () const = 0; + + NodeId node_id; +}; + +// Rust "item" AST node (declaration of top-level/module-level allowed stuff) +class Item : public Stmt +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_item () const + { + return std::unique_ptr (clone_item_impl ()); + } + + /* Adds crate names to the vector passed by reference, if it can + * (polymorphism). TODO: remove, unused. */ + virtual void + add_crate_name (std::vector &names ATTRIBUTE_UNUSED) const + {} + + // FIXME: ARTHUR: Is it okay to have removed that final? Is it *required* + // behavior that we have items that can also be expressions? + bool is_item () const override { return true; } + +protected: + // Clone function implementation as pure virtual method + virtual Item *clone_item_impl () const = 0; + + /* Save having to specify two clone methods in derived classes by making + * statement clone return item clone. Hopefully won't affect performance too + * much. */ + Item *clone_stmt_impl () const final override { return clone_item_impl (); } +}; + +// forward decl of ExprWithoutBlock +class ExprWithoutBlock; + +// Base expression AST node - abstract +class Expr : public Node +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_expr () const + { + return std::unique_ptr (clone_expr_impl ()); + } + + /* TODO: public methods that could be useful: + * - get_type() - returns type of expression. set_type() may also be useful + * for some? + * - evaluate() - evaluates expression if constant? can_evaluate()? */ + + /* HACK: downcasting without dynamic_cast (if possible) via polymorphism - + * overrided in subclasses of ExprWithoutBlock */ + virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; } + + virtual std::string as_string () const = 0; + + virtual ~Expr () {} + + virtual Location get_locus () const = 0; + + // HACK: strictly not needed, but faster than full downcast clone + virtual bool is_expr_without_block () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + + virtual NodeId get_node_id () const { return node_id; } + + virtual void set_node_id (NodeId id) { node_id = id; } + +protected: + // Constructor + Expr () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + // Clone function implementation as pure virtual method + virtual Expr *clone_expr_impl () const = 0; + + // TODO: think of less hacky way to implement this kind of thing + // Sets outer attributes. + virtual void set_outer_attrs (std::vector) = 0; + + NodeId node_id; +}; + +// AST node for an expression without an accompanying block - abstract +class ExprWithoutBlock : public Expr +{ +protected: + // pure virtual clone implementation + virtual ExprWithoutBlock *clone_expr_without_block_impl () const = 0; + + /* Save having to specify two clone methods in derived classes by making expr + * clone return exprwithoutblock clone. Hopefully won't affect performance too + * much. */ + ExprWithoutBlock *clone_expr_impl () const final override + { + return clone_expr_without_block_impl (); + } + + bool is_expr_without_block () const final override { return true; }; + +public: + // Unique pointer custom clone function + std::unique_ptr clone_expr_without_block () const + { + return std::unique_ptr (clone_expr_without_block_impl ()); + } + + /* downcasting hack from expr to use pratt parsing with + * parse_expr_without_block */ + ExprWithoutBlock *as_expr_without_block () const final override + { + return clone_expr_without_block_impl (); + } + + virtual ExprWithoutBlock *to_stmt () const { return clone_expr_impl (); } +}; + +/* HACK: IdentifierExpr, delete when figure out identifier vs expr problem in + * Pratt parser */ +/* Alternatively, identifiers could just be represented as single-segment paths + */ +class IdentifierExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + Identifier ident; + Location locus; + +public: + IdentifierExpr (Identifier ident, std::vector outer_attrs, + Location locus) + : outer_attrs (std::move (outer_attrs)), ident (std::move (ident)), + locus (locus) + {} + + std::string as_string () const override { return ident; } + + Location get_locus () const override final { return locus; } + + Identifier get_ident () const { return ident; } + + void accept_vis (ASTVisitor &vis) override; + + // Clones this object. + std::unique_ptr clone_identifier_expr () const + { + return std::unique_ptr (clone_identifier_expr_impl ()); + } + + // "Error state" if ident is empty, so base stripping on this. + void mark_for_strip () override { ident = {}; } + bool is_marked_for_strip () const override { return ident.empty (); } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + // Clone method implementation + IdentifierExpr *clone_expr_without_block_impl () const final override + { + return clone_identifier_expr_impl (); + } + + IdentifierExpr *clone_identifier_expr_impl () const + { + return new IdentifierExpr (*this); + } +}; + +// Pattern base AST node +class Pattern +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_pattern () const + { + return std::unique_ptr (clone_pattern_impl ()); + } + + // possible virtual methods: is_refutable() + + virtual ~Pattern () {} + + virtual std::string as_string () const = 0; + virtual void accept_vis (ASTVisitor &vis) = 0; + + // as only one kind of pattern can be stripped, have default of nothing + virtual void mark_for_strip () {} + virtual bool is_marked_for_strip () const { return false; } + + virtual Location get_locus () const = 0; + virtual NodeId get_pattern_node_id () const = 0; + +protected: + // Clone pattern implementation as pure virtual method + virtual Pattern *clone_pattern_impl () const = 0; +}; + +// forward decl for Type +class TraitBound; + +// Base class for types as represented in AST - abstract +class Type : public Node +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_type () const + { + return std::unique_ptr (clone_type_impl ()); + } + + // virtual destructor + virtual ~Type () {} + + virtual std::string as_string () const = 0; + + /* HACK: convert to trait bound. Virtual method overriden by classes that + * enable this. */ + virtual TraitBound *to_trait_bound (bool) const { return nullptr; } + /* as pointer, shouldn't require definition beforehand, only forward + * declaration. */ + + virtual void accept_vis (ASTVisitor &vis) = 0; + + // as only two kinds of types can be stripped, have default of nothing + virtual void mark_for_strip () {} + virtual bool is_marked_for_strip () const { return false; } + + virtual Location get_locus () const = 0; + + NodeId get_node_id () const { return node_id; } + +protected: + Type () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + // Clone function implementation as pure virtual method + virtual Type *clone_type_impl () const = 0; + + NodeId node_id; +}; + +// A type without parentheses? - abstract +class TypeNoBounds : public Type +{ +public: + // Unique pointer custom clone function + std::unique_ptr clone_type_no_bounds () const + { + return std::unique_ptr (clone_type_no_bounds_impl ()); + } + +protected: + // Clone function implementation as pure virtual method + virtual TypeNoBounds *clone_type_no_bounds_impl () const = 0; + + /* Save having to specify two clone methods in derived classes by making type + * clone return typenobounds clone. Hopefully won't affect performance too + * much. */ + TypeNoBounds *clone_type_impl () const final override + { + return clone_type_no_bounds_impl (); + } + + TypeNoBounds () : Type () {} +}; + +/* Abstract base class representing a type param bound - Lifetime and TraitBound + * extends it */ +class TypeParamBound +{ +public: + virtual ~TypeParamBound () {} + + // Unique pointer custom clone function + std::unique_ptr clone_type_param_bound () const + { + return std::unique_ptr (clone_type_param_bound_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + NodeId get_node_id () const { return node_id; } + + virtual Location get_locus () const = 0; + +protected: + // Clone function implementation as pure virtual method + virtual TypeParamBound *clone_type_param_bound_impl () const = 0; + + TypeParamBound (NodeId node_id) : node_id (node_id) {} + + NodeId node_id; +}; + +// Represents a lifetime (and is also a kind of type param bound) +class Lifetime : public TypeParamBound +{ +public: + enum LifetimeType + { + NAMED, // corresponds to LIFETIME_OR_LABEL + STATIC, // corresponds to 'static + WILDCARD // corresponds to '_ + }; + +private: + LifetimeType lifetime_type; + std::string lifetime_name; + Location locus; + NodeId node_id; + +public: + // Constructor + Lifetime (LifetimeType type, std::string name = std::string (), + Location locus = Location ()) + : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()), + lifetime_type (type), lifetime_name (std::move (name)), locus (locus) + {} + + Lifetime (NodeId id, LifetimeType type, std::string name = std::string (), + Location locus = Location ()) + : TypeParamBound (id), lifetime_type (type), + lifetime_name (std::move (name)), locus (locus) + {} + + // Creates an "error" lifetime. + static Lifetime error () { return Lifetime (NAMED, ""); } + + // Returns true if the lifetime is in an error state. + bool is_error () const + { + return lifetime_type == NAMED && lifetime_name.empty (); + } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + LifetimeType get_lifetime_type () { return lifetime_type; } + + Location get_locus () const override final { return locus; } + + std::string get_lifetime_name () const { return lifetime_name; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + Lifetime *clone_type_param_bound_impl () const override + { + return new Lifetime (node_id, lifetime_type, lifetime_name, locus); + } +}; + +/* Base generic parameter in AST. Abstract - can be represented by a Lifetime or + * Type param */ +class GenericParam +{ +public: + enum class Kind + { + Lifetime, + Type, + Const, + }; + + virtual ~GenericParam () {} + + // Unique pointer custom clone function + std::unique_ptr clone_generic_param () const + { + return std::unique_ptr (clone_generic_param_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual Location get_locus () const = 0; + + virtual Kind get_kind () const = 0; + + NodeId get_node_id () { return node_id; } + +protected: + GenericParam () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + GenericParam (NodeId node_id) : node_id (node_id) {} + + // Clone function implementation as pure virtual method + virtual GenericParam *clone_generic_param_impl () const = 0; + + NodeId node_id; +}; + +// A lifetime generic parameter (as opposed to a type generic parameter) +class LifetimeParam : public GenericParam +{ + Lifetime lifetime; + std::vector lifetime_bounds; + Attribute outer_attr; + Location locus; + +public: + Lifetime get_lifetime () const { return lifetime; } + + // Returns whether the lifetime param has any lifetime bounds. + bool has_lifetime_bounds () const { return !lifetime_bounds.empty (); } + + // Returns whether the lifetime param has an outer attribute. + bool has_outer_attribute () const { return !outer_attr.is_empty (); } + + // Creates an error state lifetime param. + static LifetimeParam create_error () + { + return LifetimeParam (Lifetime::error (), {}, Attribute::create_empty (), + Location ()); + } + + // Returns whether the lifetime param is in an error state. + bool is_error () const { return lifetime.is_error (); } + + // Constructor + LifetimeParam (Lifetime lifetime, std::vector lifetime_bounds, + Attribute outer_attr, Location locus) + : lifetime (std::move (lifetime)), + lifetime_bounds (std::move (lifetime_bounds)), + outer_attr (std::move (outer_attr)), locus (locus) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Location get_locus () const override final { return locus; } + + Kind get_kind () const override final { return Kind::Lifetime; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + LifetimeParam *clone_generic_param_impl () const override + { + return new LifetimeParam (*this); + } +}; + +// A macro item AST node - abstract base class +class MacroItem : public Item +{ +}; + +// Item used in trait declarations - abstract base class +class TraitItem +{ +protected: + TraitItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + // Clone function implementation as pure virtual method + virtual TraitItem *clone_trait_item_impl () const = 0; + + NodeId node_id; + +public: + virtual ~TraitItem () {} + + // Unique pointer custom clone function + std::unique_ptr clone_trait_item () const + { + return std::unique_ptr (clone_trait_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + + NodeId get_node_id () const { return node_id; } +}; + +/* Abstract base class for items used within an inherent impl block (the impl + * name {} one) */ +class InherentImplItem +{ +protected: + // Clone function implementation as pure virtual method + virtual InherentImplItem *clone_inherent_impl_item_impl () const = 0; + +public: + virtual ~InherentImplItem () {} + + // Unique pointer custom clone function + std::unique_ptr clone_inherent_impl_item () const + { + return std::unique_ptr (clone_inherent_impl_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + + virtual Location get_locus () const = 0; +}; + +// Abstract base class for items used in a trait impl +class TraitImplItem +{ +protected: + virtual TraitImplItem *clone_trait_impl_item_impl () const = 0; + +public: + virtual ~TraitImplItem (){}; + + // Unique pointer custom clone function + std::unique_ptr clone_trait_impl_item () const + { + return std::unique_ptr (clone_trait_impl_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; +}; + +// Abstract base class for an item used inside an extern block +class ExternalItem +{ +public: + ExternalItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + virtual ~ExternalItem () {} + + // Unique pointer custom clone function + std::unique_ptr clone_external_item () const + { + return std::unique_ptr (clone_external_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + + NodeId get_node_id () const { return node_id; } + +protected: + // Clone function implementation as pure virtual method + virtual ExternalItem *clone_external_item_impl () const = 0; + + NodeId node_id; +}; + +/* Data structure to store the data used in macro invocations and macro + * invocations with semicolons. */ +struct MacroInvocData +{ +private: + SimplePath path; + DelimTokenTree token_tree; + + // One way of parsing the macro. Probably not applicable for all macros. + std::vector > parsed_items; + bool parsed_to_meta_item = false; + +public: + std::string as_string () const; + + MacroInvocData (SimplePath path, DelimTokenTree token_tree) + : path (std::move (path)), token_tree (std::move (token_tree)) + {} + + // Copy constructor with vector clone + MacroInvocData (const MacroInvocData &other) + : path (other.path), token_tree (other.token_tree), + parsed_to_meta_item (other.parsed_to_meta_item) + { + parsed_items.reserve (other.parsed_items.size ()); + for (const auto &e : other.parsed_items) + parsed_items.push_back (e->clone_meta_item_inner ()); + } + + // Copy assignment operator with vector clone + MacroInvocData &operator= (const MacroInvocData &other) + { + path = other.path; + token_tree = other.token_tree; + parsed_to_meta_item = other.parsed_to_meta_item; + + parsed_items.reserve (other.parsed_items.size ()); + for (const auto &e : other.parsed_items) + parsed_items.push_back (e->clone_meta_item_inner ()); + + return *this; + } + + // Move constructors + MacroInvocData (MacroInvocData &&other) = default; + MacroInvocData &operator= (MacroInvocData &&other) = default; + + // Invalid if path is empty, so base stripping on that. + void mark_for_strip () { path = SimplePath::create_empty (); } + bool is_marked_for_strip () const { return path.is_empty (); } + + // Returns whether the macro has been parsed already. + bool is_parsed () const { return parsed_to_meta_item; } + // TODO: update on other ways of parsing it + + // TODO: this mutable getter seems kinda dodgy + DelimTokenTree &get_delim_tok_tree () { return token_tree; } + const DelimTokenTree &get_delim_tok_tree () const { return token_tree; } + + // TODO: this mutable getter seems kinda dodgy + SimplePath &get_path () { return path; } + const SimplePath &get_path () const { return path; } + + void + set_meta_item_output (std::vector > new_items) + { + parsed_items = std::move (new_items); + } + // TODO: mutable getter seems kinda dodgy + std::vector > &get_meta_items () + { + return parsed_items; + } + const std::vector > &get_meta_items () const + { + return parsed_items; + } +}; + +class SingleASTNode +{ +public: + enum NodeType + { + EXPRESSION, + ITEM, + STMT, + EXTERN, + TRAIT, + IMPL, + TRAIT_IMPL, + TYPE, + }; + +private: + NodeType kind; + + // FIXME make this a union + std::unique_ptr expr; + std::unique_ptr item; + std::unique_ptr stmt; + std::unique_ptr external_item; + std::unique_ptr trait_item; + std::unique_ptr impl_item; + std::unique_ptr trait_impl_item; + std::unique_ptr type; + +public: + SingleASTNode (std::unique_ptr expr) + : kind (EXPRESSION), expr (std::move (expr)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (ITEM), item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr stmt) + : kind (STMT), stmt (std::move (stmt)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (EXTERN), external_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (TRAIT), trait_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr item) + : kind (IMPL), impl_item (std::move (item)) + {} + + SingleASTNode (std::unique_ptr trait_impl_item) + : kind (TRAIT_IMPL), trait_impl_item (std::move (trait_impl_item)) + {} + + SingleASTNode (std::unique_ptr type) + : kind (TYPE), type (std::move (type)) + {} + + SingleASTNode (SingleASTNode const &other) + { + kind = other.kind; + switch (kind) + { + case EXPRESSION: + expr = other.expr->clone_expr (); + break; + + case ITEM: + item = other.item->clone_item (); + break; + + case STMT: + stmt = other.stmt->clone_stmt (); + break; + + case EXTERN: + external_item = other.external_item->clone_external_item (); + break; + + case TRAIT: + trait_item = other.trait_item->clone_trait_item (); + break; + + case IMPL: + impl_item = other.impl_item->clone_inherent_impl_item (); + break; + + case TRAIT_IMPL: + trait_impl_item = other.trait_impl_item->clone_trait_impl_item (); + break; + + case TYPE: + type = other.type->clone_type (); + break; + } + } + + SingleASTNode operator= (SingleASTNode const &other) + { + kind = other.kind; + switch (kind) + { + case EXPRESSION: + expr = other.expr->clone_expr (); + break; + + case ITEM: + item = other.item->clone_item (); + break; + + case STMT: + stmt = other.stmt->clone_stmt (); + break; + + case EXTERN: + external_item = other.external_item->clone_external_item (); + break; + + case TRAIT: + trait_item = other.trait_item->clone_trait_item (); + break; + + case IMPL: + impl_item = other.impl_item->clone_inherent_impl_item (); + break; + + case TRAIT_IMPL: + trait_impl_item = other.trait_impl_item->clone_trait_impl_item (); + break; + + case TYPE: + type = other.type->clone_type (); + break; + } + return *this; + } + + SingleASTNode (SingleASTNode &&other) = default; + SingleASTNode &operator= (SingleASTNode &&other) = default; + + NodeType get_kind () const { return kind; } + + std::unique_ptr &get_expr () + { + rust_assert (kind == EXPRESSION); + return expr; + } + + std::unique_ptr &get_item () + { + rust_assert (kind == ITEM); + return item; + } + + std::unique_ptr &get_stmt () + { + rust_assert (kind == STMT); + return stmt; + } + + /** + * Access the inner nodes and take ownership of them. + * You can only call these functions once per node + */ + + std::unique_ptr take_stmt () + { + rust_assert (!is_error ()); + return std::move (stmt); + } + + std::unique_ptr take_expr () + { + rust_assert (!is_error ()); + return std::move (expr); + } + + std::unique_ptr take_item () + { + rust_assert (!is_error ()); + return std::move (item); + } + + std::unique_ptr take_trait_item () + { + rust_assert (!is_error ()); + return std::move (trait_item); + } + + std::unique_ptr take_external_item () + { + rust_assert (!is_error ()); + return std::move (external_item); + } + + std::unique_ptr take_impl_item () + { + rust_assert (!is_error ()); + return std::move (impl_item); + } + + std::unique_ptr take_trait_impl_item () + { + rust_assert (!is_error ()); + return std::move (trait_impl_item); + } + + std::unique_ptr take_type () + { + rust_assert (!is_error ()); + return std::move (type); + } + + void accept_vis (ASTVisitor &vis) + { + switch (kind) + { + case EXPRESSION: + expr->accept_vis (vis); + break; + + case ITEM: + item->accept_vis (vis); + break; + + case STMT: + stmt->accept_vis (vis); + break; + + case EXTERN: + external_item->accept_vis (vis); + break; + + case TRAIT: + trait_item->accept_vis (vis); + break; + + case IMPL: + impl_item->accept_vis (vis); + break; + + case TRAIT_IMPL: + trait_impl_item->accept_vis (vis); + break; + + case TYPE: + type->accept_vis (vis); + break; + } + } + + bool is_error () + { + switch (kind) + { + case EXPRESSION: + return expr == nullptr; + case ITEM: + return item == nullptr; + case STMT: + return stmt == nullptr; + case EXTERN: + return external_item == nullptr; + case TRAIT: + return trait_item == nullptr; + case IMPL: + return impl_item == nullptr; + case TRAIT_IMPL: + return trait_impl_item == nullptr; + case TYPE: + return type == nullptr; + } + + gcc_unreachable (); + return true; + } + + std::string as_string () + { + switch (kind) + { + case EXPRESSION: + return "Expr: " + expr->as_string (); + case ITEM: + return "Item: " + item->as_string (); + case STMT: + return "Stmt: " + stmt->as_string (); + case EXTERN: + return "External Item: " + external_item->as_string (); + case TRAIT: + return "Trait Item: " + trait_item->as_string (); + case IMPL: + return "Impl Item: " + impl_item->as_string (); + case TRAIT_IMPL: + return "Trait Impl Item: " + trait_impl_item->as_string (); + case TYPE: + return "Type: " + type->as_string (); + } + + gcc_unreachable (); + return ""; + } +}; + +/* Basically, a "fragment" that can be incorporated into the AST, created as + * a result of macro expansion. Really annoying to work with due to the fact + * that macros can really expand to anything. As such, horrible representation + * at the moment. */ +class ASTFragment +{ +private: + /* basic idea: essentially, a vector of tagged unions of different AST node + * types. Now, this could actually be stored without a tagged union if the + * different AST node types had a unified parent, but that would create + * issues with the diamond problem or significant performance penalties. So + * a tagged union had to be used instead. A vector is used to represent the + * ability for a macro to expand to two statements, for instance. */ + + std::vector nodes; + bool fragment_is_error; + + /** + * We need to make a special case for Expression and Type fragments as only + * one Node will be extracted from the `nodes` vector + */ + + bool is_single_fragment () const { return nodes.size () == 1; } + + bool is_single_fragment_kind (SingleASTNode::NodeType kind) const + { + return is_single_fragment () && nodes[0].get_kind () == kind; + } + +public: + ASTFragment (std::vector nodes, bool fragment_is_error = false) + : nodes (std::move (nodes)), fragment_is_error (fragment_is_error) + { + if (fragment_is_error) + rust_assert (nodes.empty ()); + } + + ASTFragment (ASTFragment const &other) + : fragment_is_error (other.fragment_is_error) + { + nodes.clear (); + nodes.reserve (other.nodes.size ()); + for (auto &n : other.nodes) + { + nodes.push_back (n); + } + } + + ASTFragment &operator= (ASTFragment const &other) + { + fragment_is_error = other.fragment_is_error; + nodes.clear (); + nodes.reserve (other.nodes.size ()); + for (auto &n : other.nodes) + { + nodes.push_back (n); + } + + return *this; + } + + static ASTFragment create_error () { return ASTFragment ({}, true); } + + std::vector &get_nodes () { return nodes; } + bool is_error () const { return fragment_is_error; } + + bool should_expand () const { return !is_error (); } + + std::unique_ptr take_expression_fragment () + { + rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::EXPRESSION)); + return nodes[0].take_expr (); + } + + std::unique_ptr take_type_fragment () + { + rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::TYPE)); + return nodes[0].take_type (); + } + + void accept_vis (ASTVisitor &vis) + { + for (auto &node : nodes) + node.accept_vis (vis); + } +}; + +// A crate AST object - holds all the data for a single compilation unit +struct Crate +{ + std::vector inner_attrs; + // dodgy spacing required here + /* TODO: is it better to have a vector of items here or a module (implicit + * top-level one)? */ + std::vector > items; + + NodeId node_id; + +public: + // Constructor + Crate (std::vector > items, + std::vector inner_attrs) + : inner_attrs (std::move (inner_attrs)), items (std::move (items)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor with vector clone + Crate (Crate const &other) + : inner_attrs (other.inner_attrs), node_id (other.node_id) + { + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_item ()); + } + + ~Crate () = default; + + // Overloaded assignment operator with vector clone + Crate &operator= (Crate const &other) + { + inner_attrs = other.inner_attrs; + node_id = other.node_id; + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_item ()); + + return *this; + } + + // Move constructors + Crate (Crate &&other) = default; + Crate &operator= (Crate &&other) = default; + + // Get crate representation as string (e.g. for debugging). + std::string as_string () const; + + // Delete all crate information, e.g. if fails cfg. + void strip_crate () + { + inner_attrs.clear (); + inner_attrs.shrink_to_fit (); + + items.clear (); + items.shrink_to_fit (); + // TODO: is this the best way to do this? + } + + NodeId get_node_id () const { return node_id; } + const std::vector &get_inner_attrs () const { return inner_attrs; } +}; + +// Base path expression AST node - abstract +class PathExpr : public ExprWithoutBlock +{ +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-cond-compilation.h b/gcc/rust/ast/rust-cond-compilation.h new file mode 100644 index 00000000000..71188ef3b4b --- /dev/null +++ b/gcc/rust/ast/rust-cond-compilation.h @@ -0,0 +1,249 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_CONDCOMPILATION +#define RUST_AST_CONDCOMPILATION +// Conditional compilation-related AST stuff + +#include "rust-ast.h" + +namespace Rust { +namespace AST { +// Base conditional compilation configuration predicate thing - abstract +class ConfigurationPredicate +{ +public: + virtual ~ConfigurationPredicate () {} + + // Unique pointer custom clone function + std::unique_ptr clone_configuration_predicate () const + { + return std::unique_ptr ( + clone_configuration_predicate_impl ()); + } + + // not sure if I'll use this but here anyway + virtual void accept_vis (ASTVisitor &vis) = 0; + +protected: + // Clone function impl to be overriden in base classes + virtual ConfigurationPredicate * + clone_configuration_predicate_impl () const = 0; +}; + +// A configuration option - true if option is set, false if option is not set. +class ConfigurationOption : public ConfigurationPredicate +{ + Identifier option_name; + + // bool has_string_literal_option_body; + std::string option_value; // technically a string or raw string literal + +public: + /* Returns whether the configuration option has a "value" part of the + * key-value pair. */ + bool has_option_value () const { return !option_value.empty (); } + + // Key-value pair constructor + ConfigurationOption (Identifier option_name, std::string option_value) + : option_name (option_name), option_value (option_value) + {} + + // Name-only constructor + ConfigurationOption (Identifier option_name) : option_name (option_name) {} + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ConfigurationOption *clone_configuration_predicate_impl () const override + { + return new ConfigurationOption (*this); + } +}; + +// TODO: inline +struct ConfigurationPredicateList +{ + std::vector> predicate_list; +}; + +// Predicate that returns true if all of the supplied predicates return true. +class ConfigurationAll : public ConfigurationPredicate +{ + std::vector> + predicate_list; // inlined form + +public: + ConfigurationAll ( + std::vector> predicate_list) + : predicate_list (predicate_list) + {} + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ConfigurationAll *clone_configuration_predicate_impl () const override + { + return new ConfigurationAll (*this); + } +}; + +// Predicate that returns true if any of the supplied predicates are true. +class ConfigurationAny : public ConfigurationPredicate +{ + std::vector> + predicate_list; // inlined form + +public: + ConfigurationAny ( + std::vector> predicate_list) + : predicate_list (predicate_list) + {} + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ConfigurationAny *clone_configuration_predicate_impl () const override + { + return new ConfigurationAny (*this); + } +}; + +/* Predicate that produces the negation of a supplied other configuration + * predicate. */ +class ConfigurationNot : public ConfigurationPredicate +{ + std::unique_ptr config_to_negate; + +public: + ConfigurationNot (ConfigurationPredicate *config_to_negate) + : config_to_negate (config_to_negate) + {} + + // Copy constructor with clone + ConfigurationNot (ConfigurationNot const &other) + : config_to_negate ( + other.config_to_negate->clone_configuration_predicate ()) + {} + + // Overloaded assignment operator to clone + ConfigurationNot &operator= (ConfigurationNot const &other) + { + config_to_negate = other.config_to_negate->clone_configuration_predicate (); + + return *this; + } + + // move constructors + ConfigurationNot (ConfigurationNot &&other) = default; + ConfigurationNot &operator= (ConfigurationNot &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ConfigurationNot *clone_configuration_predicate_impl () const override + { + return new ConfigurationNot (*this); + } +}; + +// TODO: relationship to other attributes? +class CfgAttribute +{ + std::unique_ptr config_to_include; + +public: + CfgAttribute (ConfigurationPredicate *config_to_include) + : config_to_include (config_to_include) + {} + + // Copy constructor with clone + CfgAttribute (CfgAttribute const &other) + : config_to_include ( + other.config_to_include->clone_configuration_predicate ()) + {} + + // Overloaded assignment operator to clone + CfgAttribute &operator= (CfgAttribute const &other) + { + config_to_include + = other.config_to_include->clone_configuration_predicate (); + + return *this; + } + + // move constructors + CfgAttribute (CfgAttribute &&other) = default; + CfgAttribute &operator= (CfgAttribute &&other) = default; +}; +/* TODO: ok, best thing to do would be eliminating this class, making Attribute + * has a "is_cfg()" method, and having attribute path as "cfg" and AttrInput as + * ConfigurationPredicate (so make ConfigurationPredicate a subclass of + * AttrInput?). Would need special handling in parser, however. */ + +// TODO: inline +struct CfgAttrs +{ + std::vector cfg_attrs; +}; + +// TODO: relationship to other attributes? +class CfgAttrAttribute +{ + std::unique_ptr config_to_include; + std::vector cfg_attrs; + +public: + CfgAttrAttribute (ConfigurationPredicate *config_to_include, + std::vector cfg_attrs) + : config_to_include (config_to_include), cfg_attrs (cfg_attrs) + {} + + // Copy constructor with clone + CfgAttrAttribute (CfgAttrAttribute const &other) + : config_to_include ( + other.config_to_include->clone_configuration_predicate ()), + cfg_attrs (cfg_attrs) + {} + + // Overloaded assignment operator to clone + CfgAttrAttribute &operator= (CfgAttrAttribute const &other) + { + config_to_include + = other.config_to_include->clone_configuration_predicate (); + cfg_attrs = other.cfg_attrs; + + return *this; + } + + // move constructors + CfgAttrAttribute (CfgAttrAttribute &&other) = default; + CfgAttrAttribute &operator= (CfgAttrAttribute &&other) = default; +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h new file mode 100644 index 00000000000..1966a590c94 --- /dev/null +++ b/gcc/rust/ast/rust-expr.h @@ -0,0 +1,4631 @@ +#ifndef RUST_AST_EXPR_H +#define RUST_AST_EXPR_H + +#include "rust-ast.h" +#include "rust-path.h" +#include "operator.h" + +namespace Rust { +namespace AST { +/* TODO: if GCC moves to C++17 or allows boost, replace some boolean + * "has_whatever" pairs with + * optional types (std::optional or boost::optional)? */ + +// AST node for an expression with an accompanying block - abstract +class ExprWithBlock : public Expr +{ +protected: + // pure virtual clone implementation + virtual ExprWithBlock *clone_expr_with_block_impl () const = 0; + + // prevent having to define multiple clone expressions + ExprWithBlock *clone_expr_impl () const final override + { + return clone_expr_with_block_impl (); + } + + bool is_expr_without_block () const final override { return false; }; + +public: + // Unique pointer custom clone function + std::unique_ptr clone_expr_with_block () const + { + return std::unique_ptr (clone_expr_with_block_impl ()); + } +}; + +// Literals? Or literal base? +class LiteralExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + Literal literal; + Location locus; + +public: + std::string as_string () const override { return literal.as_string (); } + + Literal::LitType get_lit_type () const { return literal.get_lit_type (); } + + LiteralExpr (std::string value_as_string, Literal::LitType type, + PrimitiveCoreType type_hint, std::vector outer_attrs, + Location locus) + : outer_attrs (std::move (outer_attrs)), + literal (std::move (value_as_string), type, type_hint), locus (locus) + {} + + LiteralExpr (Literal literal, std::vector outer_attrs, + Location locus) + : outer_attrs (std::move (outer_attrs)), literal (std::move (literal)), + locus (locus) + {} + + // Unique pointer custom clone function + std::unique_ptr clone_literal_expr () const + { + return std::unique_ptr (clone_literal_expr_impl ()); + } + + Location get_locus () const override final { return locus; } + + Literal get_literal () const { return literal; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if literal is in error state, so base stripping on that. + void mark_for_strip () override { literal = Literal::create_error (); } + bool is_marked_for_strip () const override { return literal.is_error (); } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + LiteralExpr *clone_expr_without_block_impl () const final override + { + return clone_literal_expr_impl (); + } + + /* not virtual as currently no subclasses of LiteralExpr, but could be in + * future */ + /*virtual*/ LiteralExpr *clone_literal_expr_impl () const + { + return new LiteralExpr (*this); + } +}; + +// Literal expression attribute body (non-macro attribute) +class AttrInputLiteral : public AttrInput +{ + LiteralExpr literal_expr; + +public: + AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr)) + {} + + std::string as_string () const override + { + return " = " + literal_expr.as_string (); + } + + void accept_vis (ASTVisitor &vis) override; + + /* this can never be a cfg predicate - cfg and cfg_attr require a token-tree + * cfg */ + bool check_cfg_predicate (const Session &) const override { return false; } + + bool is_meta_item () const override { return false; } + + LiteralExpr &get_literal () { return literal_expr; } + + AttrInputType get_attr_input_type () const final override + { + return AttrInput::AttrInputType::LITERAL; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + AttrInputLiteral *clone_attr_input_impl () const override + { + return new AttrInputLiteral (*this); + } +}; + +/* literal expr only meta item inner - TODO possibly replace with inheritance of + * LiteralExpr itself? */ +class MetaItemLitExpr : public MetaItemInner +{ + LiteralExpr lit_expr; + +public: + MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {} + + std::string as_string () const override { return lit_expr.as_string (); } + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaItemLitExpr *clone_meta_item_inner_impl () const override + { + return new MetaItemLitExpr (*this); + } +}; + +// more generic meta item "path = lit" form +class MetaItemPathLit : public MetaItem +{ + SimplePath path; + LiteralExpr lit; + +public: + MetaItemPathLit (SimplePath path, LiteralExpr lit_expr) + : path (std::move (path)), lit (std::move (lit_expr)) + {} + + std::string as_string () const override + { + return path.as_string () + " = " + lit.as_string (); + } + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + /* TODO: return true if "ident" is defined and value of it is "lit", return + * false otherwise */ + + Attribute to_attribute () const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaItemPathLit *clone_meta_item_inner_impl () const override + { + return new MetaItemPathLit (*this); + } +}; + +/* Represents an expression using unary or binary operators as AST node. Can be + * overloaded. */ +class OperatorExpr : public ExprWithoutBlock +{ + // TODO: create binary and unary operator subclasses? +public: + Location locus; + +protected: + /* Variables must be protected to allow derived classes to use them as first + * class citizens */ + std::vector outer_attrs; + std::unique_ptr main_or_left_expr; + + // Constructor (only for initialisation of expr purposes) + OperatorExpr (std::unique_ptr main_or_left_expr, + std::vector outer_attribs, Location locus) + : locus (locus), outer_attrs (std::move (outer_attribs)), + main_or_left_expr (std::move (main_or_left_expr)) + {} + + // Copy constructor (only for initialisation of expr purposes) + OperatorExpr (OperatorExpr const &other) + : locus (other.locus), outer_attrs (other.outer_attrs) + { + // guard to prevent null dereference (only required if error state) + if (other.main_or_left_expr != nullptr) + main_or_left_expr = other.main_or_left_expr->clone_expr (); + } + + // Overload assignment operator to deep copy expr + OperatorExpr &operator= (OperatorExpr const &other) + { + ExprWithoutBlock::operator= (other); + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.main_or_left_expr != nullptr) + main_or_left_expr = other.main_or_left_expr->clone_expr (); + else + main_or_left_expr = nullptr; + + return *this; + } + + // move constructors + OperatorExpr (OperatorExpr &&other) = default; + OperatorExpr &operator= (OperatorExpr &&other) = default; + +public: + Location get_locus () const override final { return locus; } + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { main_or_left_expr = nullptr; } + bool is_marked_for_strip () const override + { + return main_or_left_expr == nullptr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } +}; + +/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be + * overloaded. */ +class BorrowExpr : public OperatorExpr +{ + bool is_mut; + bool double_borrow; + +public: + std::string as_string () const override; + + BorrowExpr (std::unique_ptr borrow_lvalue, bool is_mut_borrow, + bool is_double_borrow, std::vector outer_attribs, + Location locus) + : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs), + locus), + is_mut (is_mut_borrow), double_borrow (is_double_borrow) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_borrowed_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + bool get_is_mut () const { return is_mut; } + + bool get_is_double_borrow () const { return double_borrow; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + BorrowExpr *clone_expr_without_block_impl () const override + { + return new BorrowExpr (*this); + } +}; + +// Unary prefix * deference operator +class DereferenceExpr : public OperatorExpr +{ +public: + std::string as_string () const override; + + // Constructor calls OperatorExpr's protected constructor + DereferenceExpr (std::unique_ptr deref_lvalue, + std::vector outer_attribs, Location locus) + : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_dereferenced_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + DereferenceExpr *clone_expr_without_block_impl () const override + { + return new DereferenceExpr (*this); + } +}; + +// Unary postfix ? error propogation operator. Cannot be overloaded. +class ErrorPropagationExpr : public OperatorExpr +{ +public: + std::string as_string () const override; + + // Constructor calls OperatorExpr's protected constructor + ErrorPropagationExpr (std::unique_ptr potential_error_value, + std::vector outer_attribs, Location locus) + : OperatorExpr (std::move (potential_error_value), + std::move (outer_attribs), locus) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_propagating_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ErrorPropagationExpr *clone_expr_without_block_impl () const override + { + return new ErrorPropagationExpr (*this); + } +}; + +// Unary prefix - or ! negation or NOT operators. +class NegationExpr : public OperatorExpr +{ +public: + using ExprType = NegationOperator; + +private: + /* Note: overload negation via std::ops::Neg and not via std::ops::Not + * Negation only works for signed integer and floating-point types, NOT only + * works for boolean and integer types (via bitwise NOT) */ + ExprType expr_type; + +public: + std::string as_string () const override; + + ExprType get_expr_type () const { return expr_type; } + + // Constructor calls OperatorExpr's protected constructor + NegationExpr (std::unique_ptr negated_value, ExprType expr_kind, + std::vector outer_attribs, Location locus) + : OperatorExpr (std::move (negated_value), std::move (outer_attribs), + locus), + expr_type (expr_kind) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_negated_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + NegationExpr *clone_expr_without_block_impl () const override + { + return new NegationExpr (*this); + } +}; + +// Infix binary operators. +, -, *, /, %, &, |, ^, <<, >> +class ArithmeticOrLogicalExpr : public OperatorExpr +{ +public: + using ExprType = ArithmeticOrLogicalOperator; + +private: + // Note: overloading trait specified in comments + ExprType expr_type; + + std::unique_ptr right_expr; + +public: + std::string as_string () const override; + + ExprType get_expr_type () const { return expr_type; } + + // Constructor calls OperatorExpr's protected constructor + ArithmeticOrLogicalExpr (std::unique_ptr left_value, + std::unique_ptr right_value, + ExprType expr_kind, Location locus) + : OperatorExpr (std::move (left_value), std::vector (), locus), + expr_type (expr_kind), right_expr (std::move (right_value)) + {} + // outer attributes not allowed + + // Copy constructor - probably required due to unique pointer + ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other) + : OperatorExpr (other), expr_type (other.expr_type), + right_expr (other.right_expr->clone_expr ()) + {} + + // Overload assignment operator + ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + right_expr = other.right_expr->clone_expr (); + expr_type = other.expr_type; + + return *this; + } + + // move constructors + ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default; + ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other) + = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_left_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_right_expr () + { + rust_assert (right_expr != nullptr); + return right_expr; + } + + void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); } + void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override + { + return new ArithmeticOrLogicalExpr (*this); + } +}; + +// Infix binary comparison operators. ==, !=, <, <=, >, >= +class ComparisonExpr : public OperatorExpr +{ +public: + using ExprType = ComparisonOperator; + +private: + // Note: overloading trait specified in comments + ExprType expr_type; + + std::unique_ptr right_expr; + +public: + std::string as_string () const override; + + ExprType get_expr_type () const { return expr_type; } + + // Constructor requires pointers for polymorphism + ComparisonExpr (std::unique_ptr left_value, + std::unique_ptr right_value, ExprType comparison_kind, + Location locus) + : OperatorExpr (std::move (left_value), std::vector (), locus), + expr_type (comparison_kind), right_expr (std::move (right_value)) + {} + // outer attributes not allowed + + // Copy constructor also calls OperatorExpr's protected constructor + ComparisonExpr (ComparisonExpr const &other) + : OperatorExpr (other), expr_type (other.expr_type), + right_expr (other.right_expr->clone_expr ()) + {} + + // Overload assignment operator to deep copy + ComparisonExpr &operator= (ComparisonExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + right_expr = other.right_expr->clone_expr (); + expr_type = other.expr_type; + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + ComparisonExpr (ComparisonExpr &&other) = default; + ComparisonExpr &operator= (ComparisonExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_left_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_right_expr () + { + rust_assert (right_expr != nullptr); + return right_expr; + } + + ExprType get_kind () { return expr_type; } + + /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2) + * maybe? */ +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ComparisonExpr *clone_expr_without_block_impl () const override + { + return new ComparisonExpr (*this); + } +}; + +// Infix binary lazy boolean logical operators && and ||. +class LazyBooleanExpr : public OperatorExpr +{ +public: + using ExprType = LazyBooleanOperator; + +private: + ExprType expr_type; + + std::unique_ptr right_expr; + +public: + // Constructor calls OperatorExpr's protected constructor + LazyBooleanExpr (std::unique_ptr left_bool_expr, + std::unique_ptr right_bool_expr, ExprType expr_kind, + Location locus) + : OperatorExpr (std::move (left_bool_expr), std::vector (), + locus), + expr_type (expr_kind), right_expr (std::move (right_bool_expr)) + {} + // outer attributes not allowed + + // Copy constructor also calls OperatorExpr's protected constructor + LazyBooleanExpr (LazyBooleanExpr const &other) + : OperatorExpr (other), expr_type (other.expr_type), + right_expr (other.right_expr->clone_expr ()) + {} + + // Overload assignment operator to deep copy + LazyBooleanExpr &operator= (LazyBooleanExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + right_expr = other.right_expr->clone_expr (); + expr_type = other.expr_type; + + return *this; + } + + // move constructors + LazyBooleanExpr (LazyBooleanExpr &&other) = default; + LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default; + + std::string as_string () const override; + + ExprType get_expr_type () const { return expr_type; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_left_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_right_expr () + { + rust_assert (right_expr != nullptr); + return right_expr; + } + + ExprType get_kind () { return expr_type; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + LazyBooleanExpr *clone_expr_without_block_impl () const override + { + return new LazyBooleanExpr (*this); + } +}; + +// Binary infix "as" cast expression. +class TypeCastExpr : public OperatorExpr +{ + std::unique_ptr type_to_convert_to; + + // Note: only certain type casts allowed, outlined in reference +public: + std::string as_string () const override; + + // Constructor requires calling protected constructor of OperatorExpr + TypeCastExpr (std::unique_ptr expr_to_cast, + std::unique_ptr type_to_cast_to, Location locus) + : OperatorExpr (std::move (expr_to_cast), std::vector (), locus), + type_to_convert_to (std::move (type_to_cast_to)) + {} + // outer attributes not allowed + + // Copy constructor also requires calling protected constructor + TypeCastExpr (TypeCastExpr const &other) + : OperatorExpr (other), + type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ()) + {} + + // Overload assignment operator to deep copy + TypeCastExpr &operator= (TypeCastExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + type_to_convert_to = other.type_to_convert_to->clone_type_no_bounds (); + + return *this; + } + + // move constructors + TypeCastExpr (TypeCastExpr &&other) = default; + TypeCastExpr &operator= (TypeCastExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_casted_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type_to_cast_to () + { + rust_assert (type_to_convert_to != nullptr); + return type_to_convert_to; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TypeCastExpr *clone_expr_without_block_impl () const override + { + return new TypeCastExpr (*this); + } +}; + +// Binary assignment expression. +class AssignmentExpr : public OperatorExpr +{ + std::unique_ptr right_expr; + +public: + std::string as_string () const override; + + // Call OperatorExpr constructor to initialise left_expr + AssignmentExpr (std::unique_ptr value_to_assign_to, + std::unique_ptr value_to_assign, + std::vector outer_attribs, Location locus) + : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs), + locus), + right_expr (std::move (value_to_assign)) + {} + // outer attributes not allowed + + // Call OperatorExpr constructor in copy constructor, as well as clone + AssignmentExpr (AssignmentExpr const &other) + : OperatorExpr (other), right_expr (other.right_expr->clone_expr ()) + {} + + // Overload assignment operator to clone unique_ptr right_expr + AssignmentExpr &operator= (AssignmentExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + right_expr = other.right_expr->clone_expr (); + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + AssignmentExpr (AssignmentExpr &&other) = default; + AssignmentExpr &operator= (AssignmentExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); } + void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_left_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_right_expr () + { + rust_assert (right_expr != nullptr); + return right_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + AssignmentExpr *clone_expr_without_block_impl () const override + { + return new AssignmentExpr (*this); + } +}; + +/* Binary infix compound assignment (arithmetic or logic then assignment) + * expressions. */ +class CompoundAssignmentExpr : public OperatorExpr +{ +public: + using ExprType = CompoundAssignmentOperator; + +private: + // Note: overloading trait specified in comments + ExprType expr_type; + std::unique_ptr right_expr; + +public: + std::string as_string () const override; + + ExprType get_expr_type () const { return expr_type; } + + // Use pointers in constructor to enable polymorphism + CompoundAssignmentExpr (std::unique_ptr value_to_assign_to, + std::unique_ptr value_to_assign, + ExprType expr_kind, Location locus) + : OperatorExpr (std::move (value_to_assign_to), std::vector (), + locus), + expr_type (expr_kind), right_expr (std::move (value_to_assign)) + {} + // outer attributes not allowed + + // Have clone in copy constructor + CompoundAssignmentExpr (CompoundAssignmentExpr const &other) + : OperatorExpr (other), expr_type (other.expr_type), + right_expr (other.right_expr->clone_expr ()) + {} + + // Overload assignment operator to clone + CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other) + { + OperatorExpr::operator= (other); + // main_or_left_expr = other.main_or_left_expr->clone_expr(); + right_expr = other.right_expr->clone_expr (); + expr_type = other.expr_type; + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default; + CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_left_expr () + { + rust_assert (main_or_left_expr != nullptr); + return main_or_left_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_right_expr () + { + rust_assert (right_expr != nullptr); + return right_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + CompoundAssignmentExpr *clone_expr_without_block_impl () const override + { + return new CompoundAssignmentExpr (*this); + } +}; + +// Expression in parentheses (i.e. like literally just any 3 + (2 * 6)) +class GroupedExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::vector inner_attrs; + std::unique_ptr expr_in_parens; + Location locus; + +public: + std::string as_string () const override; + + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + GroupedExpr (std::unique_ptr parenthesised_expr, + std::vector inner_attribs, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + inner_attrs (std::move (inner_attribs)), + expr_in_parens (std::move (parenthesised_expr)), locus (locus) + {} + + // Copy constructor includes clone for expr_in_parens + GroupedExpr (GroupedExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + inner_attrs (other.inner_attrs), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.expr_in_parens != nullptr) + expr_in_parens = other.expr_in_parens->clone_expr (); + } + + // Overloaded assignment operator to clone expr_in_parens + GroupedExpr &operator= (GroupedExpr const &other) + { + ExprWithoutBlock::operator= (other); + inner_attrs = other.inner_attrs; + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.expr_in_parens != nullptr) + expr_in_parens = other.expr_in_parens->clone_expr (); + else + expr_in_parens = nullptr; + + return *this; + } + + // move constructors + GroupedExpr (GroupedExpr &&other) = default; + GroupedExpr &operator= (GroupedExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if inner expr is null, so base stripping on that. + void mark_for_strip () override { expr_in_parens = nullptr; } + bool is_marked_for_strip () const override + { + return expr_in_parens == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr_in_parens () + { + rust_assert (expr_in_parens != nullptr); + return expr_in_parens; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + GroupedExpr *clone_expr_without_block_impl () const override + { + return new GroupedExpr (*this); + } +}; + +// Base array initialisation internal element representation thing (abstract) +// aka ArrayElements +class ArrayElems +{ +public: + virtual ~ArrayElems () {} + + // Unique pointer custom clone ArrayElems function + std::unique_ptr clone_array_elems () const + { + return std::unique_ptr (clone_array_elems_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + NodeId get_node_id () const { return node_id; } + +protected: + ArrayElems () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} + + // pure virtual clone implementation + virtual ArrayElems *clone_array_elems_impl () const = 0; + + NodeId node_id; +}; + +// Value array elements +class ArrayElemsValues : public ArrayElems +{ + std::vector > values; + Location locus; + +public: + ArrayElemsValues (std::vector > elems, Location locus) + : ArrayElems (), values (std::move (elems)), locus (locus) + {} + + // copy constructor with vector clone + ArrayElemsValues (ArrayElemsValues const &other) + { + values.reserve (other.values.size ()); + for (const auto &e : other.values) + values.push_back (e->clone_expr ()); + } + + // overloaded assignment operator with vector clone + ArrayElemsValues &operator= (ArrayElemsValues const &other) + { + values.reserve (other.values.size ()); + for (const auto &e : other.values) + values.push_back (e->clone_expr ()); + + return *this; + } + + // move constructors + ArrayElemsValues (ArrayElemsValues &&other) = default; + ArrayElemsValues &operator= (ArrayElemsValues &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_values () const + { + return values; + } + std::vector > &get_values () { return values; } + + size_t get_num_values () const { return values.size (); } + +protected: + ArrayElemsValues *clone_array_elems_impl () const override + { + return new ArrayElemsValues (*this); + } +}; + +// Copied array element and number of copies +class ArrayElemsCopied : public ArrayElems +{ + std::unique_ptr elem_to_copy; + std::unique_ptr num_copies; + Location locus; + +public: + // Constructor requires pointers for polymorphism + ArrayElemsCopied (std::unique_ptr copied_elem, + std::unique_ptr copy_amount, Location locus) + : ArrayElems (), elem_to_copy (std::move (copied_elem)), + num_copies (std::move (copy_amount)), locus (locus) + {} + + // Copy constructor required due to unique_ptr - uses custom clone + ArrayElemsCopied (ArrayElemsCopied const &other) + : elem_to_copy (other.elem_to_copy->clone_expr ()), + num_copies (other.num_copies->clone_expr ()) + {} + + // Overloaded assignment operator for deep copying + ArrayElemsCopied &operator= (ArrayElemsCopied const &other) + { + elem_to_copy = other.elem_to_copy->clone_expr (); + num_copies = other.num_copies->clone_expr (); + + return *this; + } + + // move constructors + ArrayElemsCopied (ArrayElemsCopied &&other) = default; + ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_elem_to_copy () + { + rust_assert (elem_to_copy != nullptr); + return elem_to_copy; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_num_copies () + { + rust_assert (num_copies != nullptr); + return num_copies; + } + +protected: + ArrayElemsCopied *clone_array_elems_impl () const override + { + return new ArrayElemsCopied (*this); + } +}; + +// Array definition-ish expression +class ArrayExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::vector inner_attrs; + std::unique_ptr internal_elements; + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + // Constructor requires ArrayElems pointer + ArrayExpr (std::unique_ptr array_elems, + std::vector inner_attribs, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + inner_attrs (std::move (inner_attribs)), + internal_elements (std::move (array_elems)), locus (locus) + { + rust_assert (internal_elements != nullptr); + } + + // Copy constructor requires cloning ArrayElems for polymorphism to hold + ArrayExpr (ArrayExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + inner_attrs (other.inner_attrs), locus (other.locus), + marked_for_strip (other.marked_for_strip) + { + internal_elements = other.internal_elements->clone_array_elems (); + rust_assert (internal_elements != nullptr); + } + + // Overload assignment operator to clone internal_elements + ArrayExpr &operator= (ArrayExpr const &other) + { + ExprWithoutBlock::operator= (other); + inner_attrs = other.inner_attrs; + locus = other.locus; + marked_for_strip = other.marked_for_strip; + outer_attrs = other.outer_attrs; + + internal_elements = other.internal_elements->clone_array_elems (); + + rust_assert (internal_elements != nullptr); + return *this; + } + + // move constructors + ArrayExpr (ArrayExpr &&other) = default; + ArrayExpr &operator= (ArrayExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_array_elems () + { + rust_assert (internal_elements != nullptr); + return internal_elements; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ArrayExpr *clone_expr_without_block_impl () const override + { + return new ArrayExpr (*this); + } +}; + +// Aka IndexExpr (also applies to slices) +/* Apparently a[b] is equivalent to *std::ops::Index::index(&a, b) or + * *std::ops::Index::index_mut(&mut a, b) */ +/* Also apparently deref operations on a will be repeatedly applied to find an + * implementation */ +class ArrayIndexExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr array_expr; + std::unique_ptr index_expr; + Location locus; + +public: + std::string as_string () const override; + + ArrayIndexExpr (std::unique_ptr array_expr, + std::unique_ptr array_index_expr, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + array_expr (std::move (array_expr)), + index_expr (std::move (array_index_expr)), locus (locus) + {} + + // Copy constructor requires special cloning due to unique_ptr + ArrayIndexExpr (ArrayIndexExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.array_expr != nullptr) + array_expr = other.array_expr->clone_expr (); + if (other.index_expr != nullptr) + index_expr = other.index_expr->clone_expr (); + } + + // Overload assignment operator to clone unique_ptrs + ArrayIndexExpr &operator= (ArrayIndexExpr const &other) + { + ExprWithoutBlock::operator= (other); + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.array_expr != nullptr) + array_expr = other.array_expr->clone_expr (); + else + array_expr = nullptr; + if (other.index_expr != nullptr) + index_expr = other.index_expr->clone_expr (); + else + index_expr = nullptr; + + return *this; + } + + // move constructors + ArrayIndexExpr (ArrayIndexExpr &&other) = default; + ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if either expr is null, so base stripping on that. + void mark_for_strip () override + { + array_expr = nullptr; + index_expr = nullptr; + } + bool is_marked_for_strip () const override + { + return array_expr == nullptr && index_expr == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_array_expr () + { + rust_assert (array_expr != nullptr); + return array_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_index_expr () + { + rust_assert (index_expr != nullptr); + return index_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ArrayIndexExpr *clone_expr_without_block_impl () const override + { + return new ArrayIndexExpr (*this); + } +}; + +// AST representation of a tuple +class TupleExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::vector inner_attrs; + std::vector > tuple_elems; + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + TupleExpr (std::vector > tuple_elements, + std::vector inner_attribs, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + inner_attrs (std::move (inner_attribs)), + tuple_elems (std::move (tuple_elements)), locus (locus) + {} + + // copy constructor with vector clone + TupleExpr (TupleExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + inner_attrs (other.inner_attrs), locus (other.locus), + marked_for_strip (other.marked_for_strip) + { + tuple_elems.reserve (other.tuple_elems.size ()); + for (const auto &e : other.tuple_elems) + tuple_elems.push_back (e->clone_expr ()); + } + + // overloaded assignment operator to vector clone + TupleExpr &operator= (TupleExpr const &other) + { + ExprWithoutBlock::operator= (other); + outer_attrs = other.outer_attrs; + inner_attrs = other.inner_attrs; + locus = other.locus; + marked_for_strip = other.marked_for_strip; + + tuple_elems.reserve (other.tuple_elems.size ()); + for (const auto &e : other.tuple_elems) + tuple_elems.push_back (e->clone_expr ()); + + return *this; + } + + // move constructors + TupleExpr (TupleExpr &&other) = default; + TupleExpr &operator= (TupleExpr &&other) = default; + + /* Note: syntactically, can disambiguate single-element tuple from parens with + * comma, i.e. (0,) rather than (0) */ + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_tuple_elems () const + { + return tuple_elems; + } + std::vector > &get_tuple_elems () + { + return tuple_elems; + } + + bool is_unit () const { return tuple_elems.size () == 0; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleExpr *clone_expr_without_block_impl () const override + { + return new TupleExpr (*this); + } +}; + +// aka TupleIndexingExpr +// AST representation of a tuple indexing expression +class TupleIndexExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr tuple_expr; + // TupleIndex is a decimal int literal with no underscores or suffix + TupleIndex tuple_index; + + Location locus; + + // i.e. pair.0 + +public: + std::string as_string () const override; + + TupleIndex get_tuple_index () const { return tuple_index; } + + TupleIndexExpr (std::unique_ptr tuple_expr, TupleIndex index, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus) + {} + + // Copy constructor requires a clone for tuple_expr + TupleIndexExpr (TupleIndexExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + tuple_index (other.tuple_index), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.tuple_expr != nullptr) + tuple_expr = other.tuple_expr->clone_expr (); + } + + // Overload assignment operator in order to clone + TupleIndexExpr &operator= (TupleIndexExpr const &other) + { + ExprWithoutBlock::operator= (other); + tuple_index = other.tuple_index; + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.tuple_expr != nullptr) + tuple_expr = other.tuple_expr->clone_expr (); + else + tuple_expr = nullptr; + + return *this; + } + + // move constructors + TupleIndexExpr (TupleIndexExpr &&other) = default; + TupleIndexExpr &operator= (TupleIndexExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if tuple expr is null, so base stripping on that. + void mark_for_strip () override { tuple_expr = nullptr; } + bool is_marked_for_strip () const override { return tuple_expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_tuple_expr () + { + rust_assert (tuple_expr != nullptr); + return tuple_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleIndexExpr *clone_expr_without_block_impl () const override + { + return new TupleIndexExpr (*this); + } +}; + +// Base struct/tuple/union value creator AST node (abstract) +class StructExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + PathInExpression struct_name; + +protected: + // Protected constructor to allow initialising struct_name + StructExpr (PathInExpression struct_path, + std::vector outer_attribs) + : outer_attrs (std::move (outer_attribs)), + struct_name (std::move (struct_path)) + {} + +public: + const PathInExpression &get_struct_name () const { return struct_name; } + PathInExpression &get_struct_name () { return struct_name; } + + std::string as_string () const override; + + // Invalid if path is empty, so base stripping on that. + void mark_for_strip () override + { + struct_name = PathInExpression::create_error (); + } + bool is_marked_for_strip () const override { return struct_name.is_error (); } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } +}; + +// Actual AST node of the struct creator (with no fields). Not abstract! +class StructExprStruct : public StructExpr +{ + std::vector inner_attrs; + + Location locus; + +public: + std::string as_string () const override; + + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + // Constructor has to call protected constructor of base class + StructExprStruct (PathInExpression struct_path, + std::vector inner_attribs, + std::vector outer_attribs, Location locus) + : StructExpr (std::move (struct_path), std::move (outer_attribs)), + inner_attrs (std::move (inner_attribs)), locus (locus) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprStruct *clone_expr_without_block_impl () const override + { + return new StructExprStruct (*this); + } +}; + +/* AST node representing expression used to fill a struct's fields from another + * struct */ +struct StructBase +{ +private: + std::unique_ptr base_struct; + Location locus; + +public: + StructBase (std::unique_ptr base_struct_ptr, Location locus) + : base_struct (std::move (base_struct_ptr)), locus (locus) + {} + + // Copy constructor requires clone + StructBase (StructBase const &other) + { + /* HACK: gets around base_struct pointer being null (e.g. if no struct base + * exists) */ + if (other.base_struct != nullptr) + base_struct = other.base_struct->clone_expr (); + } + + // Destructor + ~StructBase () = default; + + // Overload assignment operator to clone base_struct + StructBase &operator= (StructBase const &other) + { + // prevent null pointer dereference + if (other.base_struct != nullptr) + base_struct = other.base_struct->clone_expr (); + else + base_struct = nullptr; + + return *this; + } + + // move constructors + StructBase (StructBase &&other) = default; + StructBase &operator= (StructBase &&other) = default; + + // Returns a null expr-ed StructBase - error state + static StructBase error () { return StructBase (nullptr, Location ()); } + + // Returns whether StructBase is in error state + bool is_invalid () const { return base_struct == nullptr; } + + std::string as_string () const; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_base_struct () + { + rust_assert (base_struct != nullptr); + return base_struct; + } +}; + +/* Base AST node for a single struct expression field (in struct instance + * creation) - abstract */ +class StructExprField +{ +public: + virtual ~StructExprField () {} + + // Unique pointer custom clone function + std::unique_ptr clone_struct_expr_field () const + { + return std::unique_ptr (clone_struct_expr_field_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual Location get_locus () const = 0; + + NodeId get_node_id () const { return node_id; } + +protected: + // pure virtual clone implementation + virtual StructExprField *clone_struct_expr_field_impl () const = 0; + + StructExprField () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + NodeId node_id; +}; + +// Identifier-only variant of StructExprField AST node +class StructExprFieldIdentifier : public StructExprField +{ + Identifier field_name; + Location locus; + +public: + StructExprFieldIdentifier (Identifier field_identifier, Location locus) + : StructExprField (), field_name (std::move (field_identifier)), + locus (locus) + {} + + std::string as_string () const override { return field_name; } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + Identifier get_field_name () const { return field_name; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprFieldIdentifier *clone_struct_expr_field_impl () const override + { + return new StructExprFieldIdentifier (*this); + } +}; + +/* Base AST node for a single struct expression field with an assigned value - + * abstract */ +class StructExprFieldWithVal : public StructExprField +{ + std::unique_ptr value; + +protected: + StructExprFieldWithVal (std::unique_ptr field_value) + : StructExprField (), value (std::move (field_value)) + {} + + // Copy constructor requires clone + StructExprFieldWithVal (StructExprFieldWithVal const &other) + : value (other.value->clone_expr ()) + {} + + // Overload assignment operator to clone unique_ptr + StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other) + { + value = other.value->clone_expr (); + + return *this; + } + + // move constructors + StructExprFieldWithVal (StructExprFieldWithVal &&other) = default; + StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default; + +public: + std::string as_string () const override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_value () + { + rust_assert (value != nullptr); + return value; + } +}; + +// Identifier and value variant of StructExprField AST node +class StructExprFieldIdentifierValue : public StructExprFieldWithVal +{ + Identifier field_name; + Location locus; + +public: + StructExprFieldIdentifierValue (Identifier field_identifier, + std::unique_ptr field_value, + Location locus) + : StructExprFieldWithVal (std::move (field_value)), + field_name (std::move (field_identifier)), locus (locus) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + std::string get_field_name () const { return field_name; } + + Location get_locus () const override final { return locus; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override + { + return new StructExprFieldIdentifierValue (*this); + } +}; + +// Tuple index and value variant of StructExprField AST node +class StructExprFieldIndexValue : public StructExprFieldWithVal +{ + TupleIndex index; + Location locus; + +public: + StructExprFieldIndexValue (TupleIndex tuple_index, + std::unique_ptr field_value, Location locus) + : StructExprFieldWithVal (std::move (field_value)), index (tuple_index), + locus (locus) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + TupleIndex get_index () const { return index; } + + Location get_locus () const override final { return locus; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprFieldIndexValue *clone_struct_expr_field_impl () const override + { + return new StructExprFieldIndexValue (*this); + } +}; + +// AST node of a struct creator with fields +class StructExprStructFields : public StructExprStruct +{ + // std::vector fields; + std::vector > fields; + + // bool has_struct_base; + StructBase struct_base; + +public: + std::string as_string () const override; + + bool has_struct_base () const { return !struct_base.is_invalid (); } + + // Constructor for StructExprStructFields when no struct base is used + StructExprStructFields ( + PathInExpression struct_path, + std::vector > expr_fields, Location locus, + StructBase base_struct = StructBase::error (), + std::vector inner_attribs = std::vector (), + std::vector outer_attribs = std::vector ()) + : StructExprStruct (std::move (struct_path), std::move (inner_attribs), + std::move (outer_attribs), locus), + fields (std::move (expr_fields)), struct_base (std::move (base_struct)) + {} + + // copy constructor with vector clone + StructExprStructFields (StructExprStructFields const &other) + : StructExprStruct (other), struct_base (other.struct_base) + { + fields.reserve (other.fields.size ()); + for (const auto &e : other.fields) + fields.push_back (e->clone_struct_expr_field ()); + } + + // overloaded assignment operator with vector clone + StructExprStructFields &operator= (StructExprStructFields const &other) + { + StructExprStruct::operator= (other); + struct_base = other.struct_base; + + fields.reserve (other.fields.size ()); + for (const auto &e : other.fields) + fields.push_back (e->clone_struct_expr_field ()); + + return *this; + } + + // move constructors + StructExprStructFields (StructExprStructFields &&other) = default; + StructExprStructFields &operator= (StructExprStructFields &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector > &get_fields () + { + return fields; + } + const std::vector > &get_fields () const + { + return fields; + } + + StructBase &get_struct_base () { return struct_base; } + const StructBase &get_struct_base () const { return struct_base; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprStructFields *clone_expr_without_block_impl () const override + { + return new StructExprStructFields (*this); + } +}; + +// AST node of the functional update struct creator +/* TODO: remove and replace with StructExprStructFields, except with empty + * vector of fields? */ +class StructExprStructBase : public StructExprStruct +{ + StructBase struct_base; + +public: + std::string as_string () const override; + + StructExprStructBase (PathInExpression struct_path, StructBase base_struct, + std::vector inner_attribs, + std::vector outer_attribs, Location locus) + : StructExprStruct (std::move (struct_path), std::move (inner_attribs), + std::move (outer_attribs), locus), + struct_base (std::move (base_struct)) + {} + + void accept_vis (ASTVisitor &vis) override; + + StructBase &get_struct_base () { return struct_base; } + const StructBase &get_struct_base () const { return struct_base; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructExprStructBase *clone_expr_without_block_impl () const override + { + return new StructExprStructBase (*this); + } +}; + +// Forward decl for Function - used in CallExpr +class Function; + +// Function call expression AST node +class CallExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr function; + std::vector > params; + Location locus; + +public: + Function *fndeclRef; + + std::string as_string () const override; + + CallExpr (std::unique_ptr function_expr, + std::vector > function_params, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + function (std::move (function_expr)), + params (std::move (function_params)), locus (locus) + {} + + // copy constructor requires clone + CallExpr (CallExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.function != nullptr) + function = other.function->clone_expr (); + + params.reserve (other.params.size ()); + for (const auto &e : other.params) + params.push_back (e->clone_expr ()); + } + + // Overload assignment operator to clone + CallExpr &operator= (CallExpr const &other) + { + ExprWithoutBlock::operator= (other); + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.function != nullptr) + function = other.function->clone_expr (); + else + function = nullptr; + + params.reserve (other.params.size ()); + for (const auto &e : other.params) + params.push_back (e->clone_expr ()); + + return *this; + } + + // move constructors + CallExpr (CallExpr &&other) = default; + CallExpr &operator= (CallExpr &&other) = default; + + // Returns whether function call has parameters. + bool has_params () const { return !params.empty (); } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if function expr is null, so base stripping on that. + void mark_for_strip () override { function = nullptr; } + bool is_marked_for_strip () const override { return function == nullptr; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_params () const + { + return params; + } + std::vector > &get_params () { return params; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_function_expr () + { + rust_assert (function != nullptr); + return function; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + CallExpr *clone_expr_without_block_impl () const override + { + return new CallExpr (*this); + } +}; + +// Method call expression AST node +class MethodCallExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr receiver; + PathExprSegment method_name; + std::vector > params; + Location locus; + +public: + std::string as_string () const override; + + MethodCallExpr (std::unique_ptr call_receiver, + PathExprSegment method_path, + std::vector > method_params, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + receiver (std::move (call_receiver)), + method_name (std::move (method_path)), params (std::move (method_params)), + locus (locus) + {} + + // copy constructor required due to cloning + MethodCallExpr (MethodCallExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + method_name (other.method_name), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.receiver != nullptr) + receiver = other.receiver->clone_expr (); + + params.reserve (other.params.size ()); + for (const auto &e : other.params) + params.push_back (e->clone_expr ()); + } + + // Overload assignment operator to clone receiver object + MethodCallExpr &operator= (MethodCallExpr const &other) + { + ExprWithoutBlock::operator= (other); + method_name = other.method_name; + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.receiver != nullptr) + receiver = other.receiver->clone_expr (); + else + receiver = nullptr; + + params.reserve (other.params.size ()); + for (const auto &e : other.params) + params.push_back (e->clone_expr ()); + + return *this; + } + + // move constructors + MethodCallExpr (MethodCallExpr &&other) = default; + MethodCallExpr &operator= (MethodCallExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if receiver expr is null, so base stripping on that. + void mark_for_strip () override { receiver = nullptr; } + bool is_marked_for_strip () const override { return receiver == nullptr; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_params () const + { + return params; + } + std::vector > &get_params () { return params; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_receiver_expr () + { + rust_assert (receiver != nullptr); + return receiver; + } + + const PathExprSegment &get_method_name () const { return method_name; } + PathExprSegment &get_method_name () { return method_name; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MethodCallExpr *clone_expr_without_block_impl () const override + { + return new MethodCallExpr (*this); + } +}; + +// aka FieldExpression +// Struct or union field access expression AST node +class FieldAccessExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr receiver; + Identifier field; + Location locus; + +public: + std::string as_string () const override; + + FieldAccessExpr (std::unique_ptr field_access_receiver, + Identifier field_name, std::vector outer_attribs, + Location locus) + : outer_attrs (std::move (outer_attribs)), + receiver (std::move (field_access_receiver)), + field (std::move (field_name)), locus (locus) + {} + + // Copy constructor required due to unique_ptr cloning + FieldAccessExpr (FieldAccessExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + field (other.field), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.receiver != nullptr) + receiver = other.receiver->clone_expr (); + } + + // Overload assignment operator to clone unique_ptr + FieldAccessExpr &operator= (FieldAccessExpr const &other) + { + ExprWithoutBlock::operator= (other); + field = other.field; + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.receiver != nullptr) + receiver = other.receiver->clone_expr (); + else + receiver = nullptr; + + return *this; + } + + // move constructors + FieldAccessExpr (FieldAccessExpr &&other) = default; + FieldAccessExpr &operator= (FieldAccessExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if receiver expr is null, so base stripping on that. + void mark_for_strip () override { receiver = nullptr; } + bool is_marked_for_strip () const override { return receiver == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_receiver_expr () + { + rust_assert (receiver != nullptr); + return receiver; + } + + Identifier get_field_name () const { return field; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + FieldAccessExpr *clone_expr_without_block_impl () const override + { + return new FieldAccessExpr (*this); + } +}; + +// Closure parameter data structure +struct ClosureParam +{ +private: + std::vector outer_attrs; + std::unique_ptr pattern; + + // bool has_type_given; + std::unique_ptr type; + Location locus; + +public: + // Returns whether the type of the parameter has been given. + bool has_type_given () const { return type != nullptr; } + + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Constructor for closure parameter + ClosureParam (std::unique_ptr param_pattern, Location locus, + std::unique_ptr param_type = nullptr, + std::vector outer_attrs = {}) + : outer_attrs (std::move (outer_attrs)), + pattern (std::move (param_pattern)), type (std::move (param_type)), + locus (locus) + {} + + // Copy constructor required due to cloning as a result of unique_ptrs + ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs) + { + // guard to protect from null pointer dereference + if (other.pattern != nullptr) + pattern = other.pattern->clone_pattern (); + if (other.type != nullptr) + type = other.type->clone_type (); + } + + ~ClosureParam () = default; + + // Assignment operator must be overloaded to clone as well + ClosureParam &operator= (ClosureParam const &other) + { + outer_attrs = other.outer_attrs; + + // guard to protect from null pointer dereference + if (other.pattern != nullptr) + pattern = other.pattern->clone_pattern (); + else + pattern = nullptr; + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + ClosureParam (ClosureParam &&other) = default; + ClosureParam &operator= (ClosureParam &&other) = default; + + // Returns whether closure parameter is in an error state. + bool is_error () const { return pattern == nullptr; } + + // Creates an error state closure parameter. + static ClosureParam create_error () + { + return ClosureParam (nullptr, Location ()); + } + + std::string as_string () const; + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_pattern () + { + rust_assert (pattern != nullptr); + return pattern; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (has_type_given ()); + return type; + } +}; + +// Base closure definition expression AST node - abstract +class ClosureExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + bool has_move; + std::vector params; // may be empty + Location locus; + +protected: + ClosureExpr (std::vector closure_params, bool has_move, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), has_move (has_move), + params (std::move (closure_params)), locus (locus) + {} + +public: + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_params () const { return params; } + std::vector &get_params () { return params; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } +}; + +// Represents a non-type-specified closure expression AST node +class ClosureExprInner : public ClosureExpr +{ + std::unique_ptr closure_inner; + +public: + std::string as_string () const override; + + // Constructor for a ClosureExprInner + ClosureExprInner (std::unique_ptr closure_inner_expr, + std::vector closure_params, Location locus, + bool is_move = false, + std::vector outer_attribs + = std::vector ()) + : ClosureExpr (std::move (closure_params), is_move, + std::move (outer_attribs), locus), + closure_inner (std::move (closure_inner_expr)) + {} + + // Copy constructor must be defined to allow copying via cloning of unique_ptr + ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.closure_inner != nullptr) + closure_inner = other.closure_inner->clone_expr (); + } + + // Overload assignment operator to clone closure_inner + ClosureExprInner &operator= (ClosureExprInner const &other) + { + ClosureExpr::operator= (other); + // params = other.params; + // has_move = other.has_move; + // outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.closure_inner != nullptr) + closure_inner = other.closure_inner->clone_expr (); + else + closure_inner = nullptr; + + return *this; + } + + // move constructors + ClosureExprInner (ClosureExprInner &&other) = default; + ClosureExprInner &operator= (ClosureExprInner &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if inner expr is null, so base stripping on that. + void mark_for_strip () override { closure_inner = nullptr; } + bool is_marked_for_strip () const override + { + return closure_inner == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition_expr () + { + rust_assert (closure_inner != nullptr); + return closure_inner; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ClosureExprInner *clone_expr_without_block_impl () const override + { + return new ClosureExprInner (*this); + } +}; + +// A block AST node +class BlockExpr : public ExprWithBlock +{ + std::vector outer_attrs; + std::vector inner_attrs; + std::vector > statements; + std::unique_ptr expr; + Location start_locus; + Location end_locus; + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + // Returns whether the block contains statements. + bool has_statements () const { return !statements.empty (); } + + // Returns whether the block contains a final expression. + bool has_tail_expr () const { return expr != nullptr; } + + BlockExpr (std::vector > block_statements, + std::unique_ptr block_expr, + std::vector inner_attribs, + std::vector outer_attribs, Location start_locus, + Location end_locus) + : outer_attrs (std::move (outer_attribs)), + inner_attrs (std::move (inner_attribs)), + statements (std::move (block_statements)), expr (std::move (block_expr)), + start_locus (start_locus), end_locus (end_locus) + {} + + // Copy constructor with clone + BlockExpr (BlockExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + inner_attrs (other.inner_attrs), start_locus (other.start_locus), + end_locus (other.end_locus), marked_for_strip (other.marked_for_strip) + { + // guard to protect from null pointer dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + + statements.reserve (other.statements.size ()); + for (const auto &e : other.statements) + statements.push_back (e->clone_stmt ()); + } + + // Overloaded assignment operator to clone pointer + BlockExpr &operator= (BlockExpr const &other) + { + ExprWithBlock::operator= (other); + inner_attrs = other.inner_attrs; + start_locus = other.start_locus; + end_locus = other.end_locus; + marked_for_strip = other.marked_for_strip; + outer_attrs = other.outer_attrs; + + // guard to protect from null pointer dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + else + expr = nullptr; + + statements.reserve (other.statements.size ()); + for (const auto &e : other.statements) + statements.push_back (e->clone_stmt ()); + + return *this; + } + + // move constructors + BlockExpr (BlockExpr &&other) = default; + BlockExpr &operator= (BlockExpr &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_block_expr () const + { + return std::unique_ptr (clone_block_expr_impl ()); + } + + Location get_locus () const override final { return start_locus; } + + Location get_start_locus () const { return start_locus; } + Location get_end_locus () const { return end_locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can be completely empty, so have to have a separate flag. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + size_t num_statements () const { return statements.size (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector > &get_statements () const + { + return statements; + } + std::vector > &get_statements () { return statements; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_tail_expr () + { + rust_assert (has_tail_expr ()); + return expr; + } + + // Removes the tail expression from the block. + void strip_tail_expr () { expr = nullptr; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + BlockExpr *clone_expr_with_block_impl () const final override + { + return clone_block_expr_impl (); + } + + /* This is the base method as not an abstract class - not virtual but could be + * in future if required. */ + /*virtual*/ BlockExpr *clone_block_expr_impl () const + { + return new BlockExpr (*this); + } +}; + +// Represents a type-specified closure expression AST node +class ClosureExprInnerTyped : public ClosureExpr +{ + // TODO: spec says typenobounds + std::unique_ptr return_type; + std::unique_ptr + expr; // only used because may be polymorphic in future + +public: + std::string as_string () const override; + + // Constructor potentially with a move + ClosureExprInnerTyped (std::unique_ptr closure_return_type, + std::unique_ptr closure_expr, + std::vector closure_params, + Location locus, bool is_move = false, + std::vector outer_attribs + = std::vector ()) + : ClosureExpr (std::move (closure_params), is_move, + std::move (outer_attribs), locus), + return_type (std::move (closure_return_type)), + expr (std::move (closure_expr)) + {} + + // Copy constructor requires cloning + ClosureExprInnerTyped (ClosureExprInnerTyped const &other) + : ClosureExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_block_expr (); + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + } + + // Overload assignment operator to clone unique_ptrs + ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other) + { + ClosureExpr::operator= (other); + // params = other.params; + // has_move = other.has_move; + // outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_block_expr (); + else + expr = nullptr; + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + return *this; + } + + // move constructors + ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default; + ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + /* Invalid if inner expr is null, so base stripping on that. Technically, + * type should also not be null. */ + void mark_for_strip () override { expr = nullptr; } + bool is_marked_for_strip () const override { return expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition_block () + { + rust_assert (expr != nullptr); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (return_type != nullptr); + return return_type; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ClosureExprInnerTyped *clone_expr_without_block_impl () const override + { + return new ClosureExprInnerTyped (*this); + } +}; + +// AST node representing continue expression within loops +class ContinueExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + Lifetime label; + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + // Returns true if the continue expr has a label. + bool has_label () const { return !label.is_error (); } + + // Constructor for a ContinueExpr with a label. + ContinueExpr (Lifetime label, std::vector outer_attribs, + Location locus) + : outer_attrs (std::move (outer_attribs)), label (std::move (label)), + locus (locus) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + Lifetime &get_label () { return label; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ContinueExpr *clone_expr_without_block_impl () const override + { + return new ContinueExpr (*this); + } +}; +// TODO: merge "break" and "continue"? Or even merge in "return"? + +// AST node representing break expression within loops +class BreakExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + Lifetime label; + std::unique_ptr break_expr; + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + // Returns whether the break expression has a label or not. + bool has_label () const { return !label.is_error (); } + + /* Returns whether the break expression has an expression used in the break or + * not. */ + bool has_break_expr () const { return break_expr != nullptr; } + + // Constructor for a break expression + BreakExpr (Lifetime break_label, std::unique_ptr expr_in_break, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)), + break_expr (std::move (expr_in_break)), locus (locus) + {} + + // Copy constructor defined to use clone for unique pointer + BreakExpr (BreakExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + label (other.label), locus (other.locus), + marked_for_strip (other.marked_for_strip) + { + // guard to protect from null pointer dereference + if (other.break_expr != nullptr) + break_expr = other.break_expr->clone_expr (); + } + + // Overload assignment operator to clone unique pointer + BreakExpr &operator= (BreakExpr const &other) + { + ExprWithoutBlock::operator= (other); + label = other.label; + locus = other.locus; + marked_for_strip = other.marked_for_strip; + outer_attrs = other.outer_attrs; + + // guard to protect from null pointer dereference + if (other.break_expr != nullptr) + break_expr = other.break_expr->clone_expr (); + else + break_expr = nullptr; + + return *this; + } + + // move constructors + BreakExpr (BreakExpr &&other) = default; + BreakExpr &operator= (BreakExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_break_expr () + { + rust_assert (has_break_expr ()); + return break_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + Lifetime &get_label () { return label; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + BreakExpr *clone_expr_without_block_impl () const override + { + return new BreakExpr (*this); + } +}; + +// Base range expression AST node object - abstract +class RangeExpr : public ExprWithoutBlock +{ + Location locus; + +protected: + // outer attributes not allowed before range expressions + RangeExpr (Location locus) : locus (locus) {} + +public: + Location get_locus () const override final { return locus; } + + // should never be called - error if called + void set_outer_attrs (std::vector /* new_attrs */) override + { + rust_assert (false); + } +}; + +// Range from (inclusive) and to (exclusive) expression AST node object +// aka RangeExpr; constructs a std::ops::Range object +class RangeFromToExpr : public RangeExpr +{ + std::unique_ptr from; + std::unique_ptr to; + +public: + std::string as_string () const override; + + RangeFromToExpr (std::unique_ptr range_from, + std::unique_ptr range_to, Location locus) + : RangeExpr (locus), from (std::move (range_from)), + to (std::move (range_to)) + {} + + // Copy constructor with cloning + RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + if (other.to != nullptr) + to = other.to->clone_expr (); + } + + // Overload assignment operator to clone unique pointers + RangeFromToExpr &operator= (RangeFromToExpr const &other) + { + RangeExpr::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + else + from = nullptr; + if (other.to != nullptr) + to = other.to->clone_expr (); + else + to = nullptr; + + return *this; + } + + // move constructors + RangeFromToExpr (RangeFromToExpr &&other) = default; + RangeFromToExpr &operator= (RangeFromToExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if either expr is null, so base stripping on that. + void mark_for_strip () override + { + from = nullptr; + to = nullptr; + } + bool is_marked_for_strip () const override + { + return from == nullptr && to == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_from_expr () + { + rust_assert (from != nullptr); + return from; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_to_expr () + { + rust_assert (to != nullptr); + return to; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeFromToExpr *clone_expr_without_block_impl () const override + { + return new RangeFromToExpr (*this); + } +}; + +// Range from (inclusive) expression AST node object +// constructs a std::ops::RangeFrom object +class RangeFromExpr : public RangeExpr +{ + std::unique_ptr from; + +public: + std::string as_string () const override; + + RangeFromExpr (std::unique_ptr range_from, Location locus) + : RangeExpr (locus), from (std::move (range_from)) + {} + + // Copy constructor with clone + RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + } + + // Overload assignment operator to clone unique_ptr + RangeFromExpr &operator= (RangeFromExpr const &other) + { + RangeExpr::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + else + from = nullptr; + + return *this; + } + + // move constructors + RangeFromExpr (RangeFromExpr &&other) = default; + RangeFromExpr &operator= (RangeFromExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { from = nullptr; } + bool is_marked_for_strip () const override { return from == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_from_expr () + { + rust_assert (from != nullptr); + return from; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeFromExpr *clone_expr_without_block_impl () const override + { + return new RangeFromExpr (*this); + } +}; + +// Range to (exclusive) expression AST node object +// constructs a std::ops::RangeTo object +class RangeToExpr : public RangeExpr +{ + std::unique_ptr to; + +public: + std::string as_string () const override; + + // outer attributes not allowed + RangeToExpr (std::unique_ptr range_to, Location locus) + : RangeExpr (locus), to (std::move (range_to)) + {} + + // Copy constructor with clone + RangeToExpr (RangeToExpr const &other) : RangeExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.to != nullptr) + to = other.to->clone_expr (); + } + + // Overload assignment operator to clone unique_ptr + RangeToExpr &operator= (RangeToExpr const &other) + { + RangeExpr::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.to != nullptr) + to = other.to->clone_expr (); + else + to = nullptr; + + return *this; + } + + // move constructors + RangeToExpr (RangeToExpr &&other) = default; + RangeToExpr &operator= (RangeToExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { to = nullptr; } + bool is_marked_for_strip () const override { return to == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_to_expr () + { + rust_assert (to != nullptr); + return to; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeToExpr *clone_expr_without_block_impl () const override + { + return new RangeToExpr (*this); + } +}; + +// Full range expression AST node object +// constructs a std::ops::RangeFull object +class RangeFullExpr : public RangeExpr +{ + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + RangeFullExpr (Location locus) : RangeExpr (locus) {} + // outer attributes not allowed + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeFullExpr *clone_expr_without_block_impl () const override + { + return new RangeFullExpr (*this); + } +}; + +// Range from (inclusive) and to (inclusive) expression AST node object +// aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object +class RangeFromToInclExpr : public RangeExpr +{ + std::unique_ptr from; + std::unique_ptr to; + +public: + std::string as_string () const override; + + RangeFromToInclExpr (std::unique_ptr range_from, + std::unique_ptr range_to, Location locus) + : RangeExpr (locus), from (std::move (range_from)), + to (std::move (range_to)) + {} + // outer attributes not allowed + + // Copy constructor with clone + RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + if (other.to != nullptr) + to = other.to->clone_expr (); + } + + // Overload assignment operator to use clone + RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other) + { + RangeExpr::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.from != nullptr) + from = other.from->clone_expr (); + else + from = nullptr; + if (other.to != nullptr) + to = other.to->clone_expr (); + else + to = nullptr; + + return *this; + } + + // move constructors + RangeFromToInclExpr (RangeFromToInclExpr &&other) = default; + RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if either expr is null, so base stripping on that. + void mark_for_strip () override + { + from = nullptr; + to = nullptr; + } + bool is_marked_for_strip () const override + { + return from == nullptr && to == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_from_expr () + { + rust_assert (from != nullptr); + return from; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_to_expr () + { + rust_assert (to != nullptr); + return to; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeFromToInclExpr *clone_expr_without_block_impl () const override + { + return new RangeFromToInclExpr (*this); + } +}; + +// Range to (inclusive) expression AST node object +// aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object +class RangeToInclExpr : public RangeExpr +{ + std::unique_ptr to; + +public: + std::string as_string () const override; + + RangeToInclExpr (std::unique_ptr range_to, Location locus) + : RangeExpr (locus), to (std::move (range_to)) + {} + // outer attributes not allowed + + // Copy constructor with clone + RangeToInclExpr (RangeToInclExpr const &other) : RangeExpr (other) + { + // guard to prevent null dereference (only required if error state) + if (other.to != nullptr) + to = other.to->clone_expr (); + } + + // Overload assignment operator to clone pointer + RangeToInclExpr &operator= (RangeToInclExpr const &other) + { + RangeExpr::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.to != nullptr) + to = other.to->clone_expr (); + else + to = nullptr; + + return *this; + } + + // move constructors + RangeToInclExpr (RangeToInclExpr &&other) = default; + RangeToInclExpr &operator= (RangeToInclExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { to = nullptr; } + bool is_marked_for_strip () const override { return to == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_to_expr () + { + rust_assert (to != nullptr); + return to; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangeToInclExpr *clone_expr_without_block_impl () const override + { + return new RangeToInclExpr (*this); + } +}; + +// Return expression AST node representation +class ReturnExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr return_expr; + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + /* Returns whether the object has an expression returned (i.e. not void return + * type). */ + bool has_returned_expr () const { return return_expr != nullptr; } + + // Constructor for ReturnExpr. + ReturnExpr (std::unique_ptr returned_expr, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), + return_expr (std::move (returned_expr)), locus (locus) + {} + + // Copy constructor with clone + ReturnExpr (ReturnExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus), marked_for_strip (other.marked_for_strip) + { + // guard to protect from null pointer dereference + if (other.return_expr != nullptr) + return_expr = other.return_expr->clone_expr (); + } + + // Overloaded assignment operator to clone return_expr pointer + ReturnExpr &operator= (ReturnExpr const &other) + { + ExprWithoutBlock::operator= (other); + locus = other.locus; + marked_for_strip = other.marked_for_strip; + outer_attrs = other.outer_attrs; + + // guard to protect from null pointer dereference + if (other.return_expr != nullptr) + return_expr = other.return_expr->clone_expr (); + else + return_expr = nullptr; + + return *this; + } + + // move constructors + ReturnExpr (ReturnExpr &&other) = default; + ReturnExpr &operator= (ReturnExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_returned_expr () + { + rust_assert (return_expr != nullptr); + return return_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ReturnExpr *clone_expr_without_block_impl () const override + { + return new ReturnExpr (*this); + } +}; + +// Forward decl - defined in rust-macro.h +class MacroInvocation; + +// An unsafe block AST node +class UnsafeBlockExpr : public ExprWithBlock +{ + std::vector outer_attrs; + // Or just have it extend BlockExpr + std::unique_ptr expr; + Location locus; + +public: + std::string as_string () const override; + + UnsafeBlockExpr (std::unique_ptr block_expr, + std::vector outer_attribs, Location locus) + : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)), + locus (locus) + {} + + // Copy constructor with clone + UnsafeBlockExpr (UnsafeBlockExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_block_expr (); + } + + // Overloaded assignment operator to clone + UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other) + { + ExprWithBlock::operator= (other); + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_block_expr (); + else + expr = nullptr; + + return *this; + } + + // move constructors + UnsafeBlockExpr (UnsafeBlockExpr &&other) = default; + UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { expr = nullptr; } + bool is_marked_for_strip () const override { return expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_block_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + UnsafeBlockExpr *clone_expr_with_block_impl () const override + { + return new UnsafeBlockExpr (*this); + } +}; + +// Loop label expression AST node used with break and continue expressions +// TODO: inline? +class LoopLabel /*: public Node*/ +{ + Lifetime label; // or type LIFETIME_OR_LABEL + Location locus; + + NodeId node_id; + +public: + std::string as_string () const; + + LoopLabel (Lifetime loop_label, Location locus = Location ()) + : label (std::move (loop_label)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Returns whether the LoopLabel is in an error state. + bool is_error () const { return label.is_error (); } + + // Creates an error state LoopLabel. + static LoopLabel error () { return LoopLabel (Lifetime::error ()); } + + Location get_locus () const { return locus; } + + Lifetime &get_lifetime () { return label; } + + NodeId get_node_id () const { return node_id; } +}; + +// Base loop expression AST node - aka LoopExpr +class BaseLoopExpr : public ExprWithBlock +{ +protected: + // protected to allow subclasses better use of them + std::vector outer_attrs; + LoopLabel loop_label; + std::unique_ptr loop_block; + +private: + Location locus; + +protected: + // Constructor for BaseLoopExpr + BaseLoopExpr (std::unique_ptr loop_block, Location locus, + LoopLabel loop_label = LoopLabel::error (), + std::vector outer_attribs + = std::vector ()) + : outer_attrs (std::move (outer_attribs)), + loop_label (std::move (loop_label)), loop_block (std::move (loop_block)), + locus (locus) + {} + + // Copy constructor for BaseLoopExpr with clone + BaseLoopExpr (BaseLoopExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + loop_label (other.loop_label), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.loop_block != nullptr) + loop_block = other.loop_block->clone_block_expr (); + } + + // Overloaded assignment operator to clone + BaseLoopExpr &operator= (BaseLoopExpr const &other) + { + ExprWithBlock::operator= (other); + loop_label = other.loop_label; + locus = other.locus; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.loop_block != nullptr) + loop_block = other.loop_block->clone_block_expr (); + else + loop_block = nullptr; + + return *this; + } + + // move constructors + BaseLoopExpr (BaseLoopExpr &&other) = default; + BaseLoopExpr &operator= (BaseLoopExpr &&other) = default; + +public: + bool has_loop_label () const { return !loop_label.is_error (); } + + LoopLabel &get_loop_label () { return loop_label; } + + Location get_locus () const override final { return locus; } + + // Invalid if loop block is null, so base stripping on that. + void mark_for_strip () override { loop_block = nullptr; } + bool is_marked_for_strip () const override { return loop_block == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_loop_block () + { + rust_assert (loop_block != nullptr); + return loop_block; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } +}; + +// 'Loop' expression (i.e. the infinite loop) AST node +class LoopExpr : public BaseLoopExpr +{ +public: + std::string as_string () const override; + + // Constructor for LoopExpr + LoopExpr (std::unique_ptr loop_block, Location locus, + LoopLabel loop_label = LoopLabel::error (), + std::vector outer_attribs = std::vector ()) + : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), + std::move (outer_attribs)) + {} + + void accept_vis (ASTVisitor &vis) override; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + LoopExpr *clone_expr_with_block_impl () const override + { + return new LoopExpr (*this); + } +}; + +// While loop expression AST node (predicate loop) +class WhileLoopExpr : public BaseLoopExpr +{ + std::unique_ptr condition; + +public: + std::string as_string () const override; + + // Constructor for while loop with loop label + WhileLoopExpr (std::unique_ptr loop_condition, + std::unique_ptr loop_block, Location locus, + LoopLabel loop_label = LoopLabel::error (), + std::vector outer_attribs + = std::vector ()) + : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), + std::move (outer_attribs)), + condition (std::move (loop_condition)) + {} + + // Copy constructor with clone + WhileLoopExpr (WhileLoopExpr const &other) + : BaseLoopExpr (other), condition (other.condition->clone_expr ()) + {} + + // Overloaded assignment operator to clone + WhileLoopExpr &operator= (WhileLoopExpr const &other) + { + BaseLoopExpr::operator= (other); + condition = other.condition->clone_expr (); + // loop_block = other.loop_block->clone_block_expr(); + // loop_label = other.loop_label; + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + WhileLoopExpr (WhileLoopExpr &&other) = default; + WhileLoopExpr &operator= (WhileLoopExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_predicate_expr () + { + rust_assert (condition != nullptr); + return condition; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + WhileLoopExpr *clone_expr_with_block_impl () const override + { + return new WhileLoopExpr (*this); + } +}; + +// While let loop expression AST node (predicate pattern loop) +class WhileLetLoopExpr : public BaseLoopExpr +{ + // MatchArmPatterns patterns; + std::vector > match_arm_patterns; // inlined + std::unique_ptr scrutinee; + +public: + std::string as_string () const override; + + // Constructor with a loop label + WhileLetLoopExpr (std::vector > match_arm_patterns, + std::unique_ptr scrutinee, + std::unique_ptr loop_block, Location locus, + LoopLabel loop_label = LoopLabel::error (), + std::vector outer_attribs + = std::vector ()) + : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), + std::move (outer_attribs)), + match_arm_patterns (std::move (match_arm_patterns)), + scrutinee (std::move (scrutinee)) + {} + + // Copy constructor with clone + WhileLetLoopExpr (WhileLetLoopExpr const &other) + : BaseLoopExpr (other), + /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee ( + other.scrutinee->clone_expr ()) + { + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator to clone pointers + WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other) + { + BaseLoopExpr::operator= (other); + // match_arm_patterns = other.match_arm_patterns; + scrutinee = other.scrutinee->clone_expr (); + // loop_block = other.loop_block->clone_block_expr(); + // loop_label = other.loop_label; + // outer_attrs = other.outer_attrs; + + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + WhileLetLoopExpr (WhileLetLoopExpr &&other) = default; + WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_scrutinee_expr () + { + rust_assert (scrutinee != nullptr); + return scrutinee; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_patterns () const + { + return match_arm_patterns; + } + std::vector > &get_patterns () + { + return match_arm_patterns; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + WhileLetLoopExpr *clone_expr_with_block_impl () const override + { + return new WhileLetLoopExpr (*this); + } +}; + +// For loop expression AST node (iterator loop) +class ForLoopExpr : public BaseLoopExpr +{ + std::unique_ptr pattern; + std::unique_ptr iterator_expr; + +public: + std::string as_string () const override; + + // Constructor with loop label + ForLoopExpr (std::unique_ptr loop_pattern, + std::unique_ptr iterator_expr, + std::unique_ptr loop_body, Location locus, + LoopLabel loop_label = LoopLabel::error (), + std::vector outer_attribs = std::vector ()) + : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label), + std::move (outer_attribs)), + pattern (std::move (loop_pattern)), + iterator_expr (std::move (iterator_expr)) + {} + + // Copy constructor with clone + ForLoopExpr (ForLoopExpr const &other) + : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()), + iterator_expr (other.iterator_expr->clone_expr ()) + {} + + // Overloaded assignment operator to clone + ForLoopExpr &operator= (ForLoopExpr const &other) + { + BaseLoopExpr::operator= (other); + pattern = other.pattern->clone_pattern (); + iterator_expr = other.iterator_expr->clone_expr (); + /*loop_block = other.loop_block->clone_block_expr(); + loop_label = other.loop_label; + outer_attrs = other.outer_attrs;*/ + + return *this; + } + + // move constructors + ForLoopExpr (ForLoopExpr &&other) = default; + ForLoopExpr &operator= (ForLoopExpr &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_iterator_expr () + { + rust_assert (iterator_expr != nullptr); + return iterator_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_pattern () + { + rust_assert (pattern != nullptr); + return pattern; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ForLoopExpr *clone_expr_with_block_impl () const override + { + return new ForLoopExpr (*this); + } +}; + +// forward decl for IfExpr +class IfLetExpr; + +// Base if expression with no "else" or "if let" AST node +class IfExpr : public ExprWithBlock +{ + std::vector outer_attrs; + std::unique_ptr condition; + std::unique_ptr if_block; + Location locus; + +public: + std::string as_string () const override; + + IfExpr (std::unique_ptr condition, std::unique_ptr if_block, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)), + if_block (std::move (if_block)), locus (locus) + {} + // outer attributes are never allowed on IfExprs + + // Copy constructor with clone + IfExpr (IfExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.condition != nullptr) + condition = other.condition->clone_expr (); + if (other.if_block != nullptr) + if_block = other.if_block->clone_block_expr (); + } + + // Overloaded assignment operator to clone expressions + IfExpr &operator= (IfExpr const &other) + { + ExprWithBlock::operator= (other); + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.condition != nullptr) + condition = other.condition->clone_expr (); + else + condition = nullptr; + if (other.if_block != nullptr) + if_block = other.if_block->clone_block_expr (); + else + if_block = nullptr; + + return *this; + } + + // move constructors + IfExpr (IfExpr &&other) = default; + IfExpr &operator= (IfExpr &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_if_expr () const + { + return std::unique_ptr (clone_if_expr_impl ()); + } + + /* Note that multiple "else if"s are handled via nested ASTs rather than a + * vector of else ifs - i.e. not like a switch statement. TODO - is this a + * better approach? or does it not parse correctly and have downsides? */ + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); } + void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_condition_expr () + { + rust_assert (condition != nullptr); + return condition; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_if_block () + { + rust_assert (if_block != nullptr); + return if_block; + } + + // Invalid if if block or condition is null, so base stripping on that. + void mark_for_strip () override + { + if_block = nullptr; + condition = nullptr; + } + bool is_marked_for_strip () const override + { + return if_block == nullptr && condition == nullptr; + } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + +protected: + // Base clone function but still concrete as concrete base class + virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); } + + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfExpr *clone_expr_with_block_impl () const final override + { + return clone_if_expr_impl (); + } +}; + +// If expression with an ending "else" expression AST node (trailing) +class IfExprConseqElse : public IfExpr +{ + std::unique_ptr else_block; + +public: + std::string as_string () const override; + + IfExprConseqElse (std::unique_ptr condition, + std::unique_ptr if_block, + std::unique_ptr else_block, + std::vector outer_attrs, Location locus) + : IfExpr (std::move (condition), std::move (if_block), + std::move (outer_attrs), locus), + else_block (std::move (else_block)) + {} + // again, outer attributes not allowed + + // Copy constructor with clone + IfExprConseqElse (IfExprConseqElse const &other) + : IfExpr (other), else_block (other.else_block->clone_block_expr ()) + {} + + // Overloaded assignment operator with cloning + IfExprConseqElse &operator= (IfExprConseqElse const &other) + { + IfExpr::operator= (other); + // condition = other.condition->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + else_block = other.else_block->clone_block_expr (); + + return *this; + } + + // move constructors + IfExprConseqElse (IfExprConseqElse &&other) = default; + IfExprConseqElse &operator= (IfExprConseqElse &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_else_block () + { + rust_assert (else_block != nullptr); + return else_block; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfExprConseqElse *clone_if_expr_impl () const override + { + return new IfExprConseqElse (*this); + } +}; + +// If expression with an ending "else if" expression AST node +class IfExprConseqIf : public IfExpr +{ + std::unique_ptr conseq_if_expr; + +public: + std::string as_string () const override; + + IfExprConseqIf (std::unique_ptr condition, + std::unique_ptr if_block, + std::unique_ptr conseq_if_expr, + std::vector outer_attrs, Location locus) + : IfExpr (std::move (condition), std::move (if_block), + std::move (outer_attrs), locus), + conseq_if_expr (std::move (conseq_if_expr)) + {} + // outer attributes not allowed + + // Copy constructor with clone + IfExprConseqIf (IfExprConseqIf const &other) + : IfExpr (other), conseq_if_expr (other.conseq_if_expr->clone_if_expr ()) + {} + + // Overloaded assignment operator to use clone + IfExprConseqIf &operator= (IfExprConseqIf const &other) + { + IfExpr::operator= (other); + // condition = other.condition->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + conseq_if_expr = other.conseq_if_expr->clone_if_expr (); + + return *this; + } + + // move constructors + IfExprConseqIf (IfExprConseqIf &&other) = default; + IfExprConseqIf &operator= (IfExprConseqIf &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + void vis_conseq_if_expr (ASTVisitor &vis) + { + conseq_if_expr->accept_vis (vis); + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_conseq_if_expr () + { + rust_assert (conseq_if_expr != nullptr); + return conseq_if_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfExprConseqIf *clone_if_expr_impl () const override + { + return new IfExprConseqIf (*this); + } +}; + +// Basic "if let" expression AST node with no else +class IfLetExpr : public ExprWithBlock +{ + std::vector outer_attrs; + std::vector > match_arm_patterns; // inlined + std::unique_ptr value; + std::unique_ptr if_block; + Location locus; + +public: + std::string as_string () const override; + + IfLetExpr (std::vector > match_arm_patterns, + std::unique_ptr value, std::unique_ptr if_block, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), + match_arm_patterns (std::move (match_arm_patterns)), + value (std::move (value)), if_block (std::move (if_block)), locus (locus) + {} + + // copy constructor with clone + IfLetExpr (IfLetExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.value != nullptr) + value = other.value->clone_expr (); + if (other.if_block != nullptr) + if_block = other.if_block->clone_block_expr (); + + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + } + + // overload assignment operator to clone + IfLetExpr &operator= (IfLetExpr const &other) + { + ExprWithBlock::operator= (other); + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.value != nullptr) + value = other.value->clone_expr (); + else + value = nullptr; + if (other.if_block != nullptr) + if_block = other.if_block->clone_block_expr (); + else + if_block = nullptr; + + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + IfLetExpr (IfLetExpr &&other) = default; + IfLetExpr &operator= (IfLetExpr &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_if_let_expr () const + { + return std::unique_ptr (clone_if_let_expr_impl ()); + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block or value is null, so base stripping on that. + void mark_for_strip () override + { + if_block = nullptr; + value = nullptr; + } + bool is_marked_for_strip () const override + { + return if_block == nullptr && value == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_value_expr () + { + rust_assert (value != nullptr); + return value; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_if_block () + { + rust_assert (if_block != nullptr); + return if_block; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_patterns () const + { + return match_arm_patterns; + } + std::vector > &get_patterns () + { + return match_arm_patterns; + } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base (or rather this or any derived object) */ + IfLetExpr *clone_expr_with_block_impl () const final override + { + return clone_if_let_expr_impl (); + } + + // Base clone function but still concrete as concrete base class + virtual IfLetExpr *clone_if_let_expr_impl () const + { + return new IfLetExpr (*this); + } +}; + +// If expression with an ending "else if let" expression AST node +class IfExprConseqIfLet : public IfExpr +{ + std::unique_ptr if_let_expr; + +public: + std::string as_string () const override; + + IfExprConseqIfLet (std::unique_ptr condition, + std::unique_ptr if_block, + std::unique_ptr conseq_if_let_expr, + std::vector outer_attrs, Location locus) + : IfExpr (std::move (condition), std::move (if_block), + std::move (outer_attrs), locus), + if_let_expr (std::move (conseq_if_let_expr)) + {} + // outer attributes not allowed + + // Copy constructor with clone + IfExprConseqIfLet (IfExprConseqIfLet const &other) + : IfExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ()) + {} + + // Overloaded assignment operator to use clone + IfExprConseqIfLet &operator= (IfExprConseqIfLet const &other) + { + IfExpr::operator= (other); + // condition = other.condition->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + if_let_expr = other.if_let_expr->clone_if_let_expr (); + + return *this; + } + + // move constructors + IfExprConseqIfLet (IfExprConseqIfLet &&other) = default; + IfExprConseqIfLet &operator= (IfExprConseqIfLet &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_conseq_if_let_expr () + { + rust_assert (if_let_expr != nullptr); + return if_let_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfExprConseqIfLet *clone_if_expr_impl () const override + { + return new IfExprConseqIfLet (*this); + } +}; + +/* AST node representing "if let" expression with an "else" expression at the + * end */ +class IfLetExprConseqElse : public IfLetExpr +{ + std::unique_ptr else_block; + +public: + std::string as_string () const override; + + IfLetExprConseqElse ( + std::vector > match_arm_patterns, + std::unique_ptr value, std::unique_ptr if_block, + std::unique_ptr else_block, std::vector outer_attrs, + Location locus) + : IfLetExpr (std::move (match_arm_patterns), std::move (value), + std::move (if_block), std::move (outer_attrs), locus), + else_block (std::move (else_block)) + {} + // outer attributes not allowed + + // copy constructor with clone + IfLetExprConseqElse (IfLetExprConseqElse const &other) + : IfLetExpr (other), else_block (other.else_block->clone_block_expr ()) + {} + + // overload assignment operator to clone + IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other) + { + IfLetExpr::operator= (other); + // match_arm_patterns = other.match_arm_patterns; + // value = other.value->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + else_block = other.else_block->clone_block_expr (); + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + IfLetExprConseqElse (IfLetExprConseqElse &&other) = default; + IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_else_block () + { + rust_assert (else_block != nullptr); + return else_block; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfLetExprConseqElse *clone_if_let_expr_impl () const override + { + return new IfLetExprConseqElse (*this); + } +}; + +/* AST node representing "if let" expression with an "else if" expression at the + * end */ +class IfLetExprConseqIf : public IfLetExpr +{ + std::unique_ptr if_expr; + +public: + std::string as_string () const override; + + IfLetExprConseqIf (std::vector > match_arm_patterns, + std::unique_ptr value, + std::unique_ptr if_block, + std::unique_ptr if_expr, + std::vector outer_attrs, Location locus) + : IfLetExpr (std::move (match_arm_patterns), std::move (value), + std::move (if_block), std::move (outer_attrs), locus), + if_expr (std::move (if_expr)) + {} + // again, outer attributes not allowed + + // copy constructor with clone + IfLetExprConseqIf (IfLetExprConseqIf const &other) + : IfLetExpr (other), if_expr (other.if_expr->clone_if_expr ()) + {} + + // overload assignment operator to clone + IfLetExprConseqIf &operator= (IfLetExprConseqIf const &other) + { + IfLetExpr::operator= (other); + // match_arm_patterns = other.match_arm_patterns; + // value = other.value->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + if_expr = other.if_expr->clone_if_expr (); + + return *this; + } + + // move constructors + IfLetExprConseqIf (IfLetExprConseqIf &&other) = default; + IfLetExprConseqIf &operator= (IfLetExprConseqIf &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_conseq_if_expr () + { + rust_assert (if_expr != nullptr); + return if_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfLetExprConseqIf *clone_if_let_expr_impl () const override + { + return new IfLetExprConseqIf (*this); + } +}; + +/* AST node representing "if let" expression with an "else if let" expression at + * the end */ +class IfLetExprConseqIfLet : public IfLetExpr +{ + std::unique_ptr if_let_expr; + +public: + std::string as_string () const override; + + IfLetExprConseqIfLet ( + std::vector > match_arm_patterns, + std::unique_ptr value, std::unique_ptr if_block, + std::unique_ptr if_let_expr, std::vector outer_attrs, + Location locus) + : IfLetExpr (std::move (match_arm_patterns), std::move (value), + std::move (if_block), std::move (outer_attrs), locus), + if_let_expr (std::move (if_let_expr)) + {} + // outer attributes not allowed + + // copy constructor with clone + IfLetExprConseqIfLet (IfLetExprConseqIfLet const &other) + : IfLetExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ()) + {} + + // overload assignment operator to clone + IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet const &other) + { + IfLetExpr::operator= (other); + // match_arm_patterns = other.match_arm_patterns; + // value = other.value->clone_expr(); + // if_block = other.if_block->clone_block_expr(); + if_let_expr = other.if_let_expr->clone_if_let_expr (); + + return *this; + } + + // move constructors + IfLetExprConseqIfLet (IfLetExprConseqIfLet &&other) = default; + IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_conseq_if_let_expr () + { + rust_assert (if_let_expr != nullptr); + return if_let_expr; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IfLetExprConseqIfLet *clone_if_let_expr_impl () const override + { + return new IfLetExprConseqIfLet (*this); + } +}; + +// Match arm expression +struct MatchArm +{ +private: + std::vector outer_attrs; + // MatchArmPatterns patterns; + std::vector > match_arm_patterns; // inlined + + // bool has_match_arm_guard; + // inlined from MatchArmGuard + std::unique_ptr guard_expr; + + Location locus; + +public: + // Returns whether the MatchArm has a match arm guard expression + bool has_match_arm_guard () const { return guard_expr != nullptr; } + + // Constructor for match arm with a guard expression + MatchArm (std::vector > match_arm_patterns, + Location locus, std::unique_ptr guard_expr = nullptr, + std::vector outer_attrs = std::vector ()) + : outer_attrs (std::move (outer_attrs)), + match_arm_patterns (std::move (match_arm_patterns)), + guard_expr (std::move (guard_expr)), locus (locus) + {} + + // Copy constructor with clone + MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs) + { + // guard to protect from null pointer dereference + if (other.guard_expr != nullptr) + guard_expr = other.guard_expr->clone_expr (); + + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + + locus = other.locus; + } + + ~MatchArm () = default; + + // Overload assignment operator to clone + MatchArm &operator= (MatchArm const &other) + { + outer_attrs = other.outer_attrs; + + if (other.guard_expr != nullptr) + guard_expr = other.guard_expr->clone_expr (); + else + guard_expr = nullptr; + + match_arm_patterns.reserve (other.match_arm_patterns.size ()); + for (const auto &e : other.match_arm_patterns) + match_arm_patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + MatchArm (MatchArm &&other) = default; + MatchArm &operator= (MatchArm &&other) = default; + + // Returns whether match arm is in an error state. + bool is_error () const { return match_arm_patterns.empty (); } + + // Creates a match arm in an error state. + static MatchArm create_error () + { + Location locus = Location (); + return MatchArm (std::vector > (), locus); + } + + std::string as_string () const; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_guard_expr () + { + rust_assert (has_match_arm_guard ()); + return guard_expr; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + const std::vector > &get_patterns () const + { + return match_arm_patterns; + } + std::vector > &get_patterns () + { + return match_arm_patterns; + } + + Location get_locus () const { return locus; } +}; + +/* A "match case" - a correlated match arm and resulting expression. Not + * abstract. */ +struct MatchCase +{ +private: + MatchArm arm; + std::unique_ptr expr; + NodeId node_id; + + /* TODO: does whether trailing comma exists need to be stored? currently + * assuming it is only syntactical and has no effect on meaning. */ + +public: + MatchCase (MatchArm arm, std::unique_ptr expr) + : arm (std::move (arm)), expr (std::move (expr)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + MatchCase (const MatchCase &other) + : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id) + {} + + MatchCase &operator= (const MatchCase &other) + { + arm = other.arm; + expr = other.expr->clone_expr (); + node_id = other.node_id; + + return *this; + } + + MatchCase (MatchCase &&other) = default; + MatchCase &operator= (MatchCase &&other) = default; + + ~MatchCase () = default; + + std::string as_string () const; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + MatchArm &get_arm () + { + rust_assert (!arm.is_error ()); + return arm; + } + + NodeId get_node_id () const { return node_id; } +}; + +// Match expression AST node +class MatchExpr : public ExprWithBlock +{ + std::vector outer_attrs; + std::unique_ptr branch_value; + std::vector inner_attrs; + std::vector match_arms; + Location locus; + +public: + std::string as_string () const override; + + // Returns whether the match expression has any match arms. + bool has_match_arms () const { return !match_arms.empty (); } + + MatchExpr (std::unique_ptr branch_value, + std::vector match_arms, + std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), + branch_value (std::move (branch_value)), + inner_attrs (std::move (inner_attrs)), + match_arms (std::move (match_arms)), locus (locus) + {} + + // Copy constructor requires clone due to unique_ptr + MatchExpr (MatchExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + inner_attrs (other.inner_attrs), match_arms (other.match_arms), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.branch_value != nullptr) + branch_value = other.branch_value->clone_expr (); + } + + // Overloaded assignment operator to clone due to unique_ptr + MatchExpr &operator= (MatchExpr const &other) + { + ExprWithBlock::operator= (other); + inner_attrs = other.inner_attrs; + match_arms = other.match_arms; + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.branch_value != nullptr) + branch_value = other.branch_value->clone_expr (); + else + branch_value = nullptr; + + return *this; + } + + // move constructors + MatchExpr (MatchExpr &&other) = default; + MatchExpr &operator= (MatchExpr &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if branch value is null, so base stripping on that. + void mark_for_strip () override { branch_value = nullptr; } + bool is_marked_for_strip () const override { return branch_value == nullptr; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_scrutinee_expr () + { + rust_assert (branch_value != nullptr); + return branch_value; + } + + const std::vector &get_match_cases () const { return match_arms; } + std::vector &get_match_cases () { return match_arms; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MatchExpr *clone_expr_with_block_impl () const override + { + return new MatchExpr (*this); + } +}; + +// Await expression AST node (pseudo-member variable access) +class AwaitExpr : public ExprWithoutBlock +{ + std::vector outer_attrs; + std::unique_ptr awaited_expr; + Location locus; + +public: + // TODO: ensure outer attributes are actually allowed + AwaitExpr (std::unique_ptr awaited_expr, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), + awaited_expr (std::move (awaited_expr)), locus (locus) + {} + + // copy constructor with clone + AwaitExpr (AwaitExpr const &other) + : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.awaited_expr != nullptr) + awaited_expr = other.awaited_expr->clone_expr (); + } + + // overloaded assignment operator with clone + AwaitExpr &operator= (AwaitExpr const &other) + { + ExprWithoutBlock::operator= (other); + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.awaited_expr != nullptr) + awaited_expr = other.awaited_expr->clone_expr (); + else + awaited_expr = nullptr; + + return *this; + } + + // move constructors + AwaitExpr (AwaitExpr &&other) = default; + AwaitExpr &operator= (AwaitExpr &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if awaited expr is null, so base stripping on that. + void mark_for_strip () override { awaited_expr = nullptr; } + bool is_marked_for_strip () const override { return awaited_expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_awaited_expr () + { + rust_assert (awaited_expr != nullptr); + return awaited_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + AwaitExpr *clone_expr_without_block_impl () const override + { + return new AwaitExpr (*this); + } +}; + +// Async block expression AST node (block expr that evaluates to a future) +class AsyncBlockExpr : public ExprWithBlock +{ + // TODO: should this extend BlockExpr rather than be a composite of it? + std::vector outer_attrs; + bool has_move; + std::unique_ptr block_expr; + Location locus; + +public: + AsyncBlockExpr (std::unique_ptr block_expr, bool has_move, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), has_move (has_move), + block_expr (std::move (block_expr)), locus (locus) + {} + + // copy constructor with clone + AsyncBlockExpr (AsyncBlockExpr const &other) + : ExprWithBlock (other), outer_attrs (other.outer_attrs), + has_move (other.has_move), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + } + + // overloaded assignment operator to clone + AsyncBlockExpr &operator= (AsyncBlockExpr const &other) + { + ExprWithBlock::operator= (other); + outer_attrs = other.outer_attrs; + has_move = other.has_move; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + else + block_expr = nullptr; + + return *this; + } + + // move constructors + AsyncBlockExpr (AsyncBlockExpr &&other) = default; + AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { block_expr = nullptr; } + bool is_marked_for_strip () const override { return block_expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_block_expr () + { + rust_assert (block_expr != nullptr); + return block_expr; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + AsyncBlockExpr *clone_expr_with_block_impl () const override + { + return new AsyncBlockExpr (*this); + } +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h new file mode 100644 index 00000000000..4987674cba1 --- /dev/null +++ b/gcc/rust/ast/rust-item.h @@ -0,0 +1,4382 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_ITEM_H +#define RUST_AST_ITEM_H + +#include "rust-ast.h" +#include "rust-path.h" +#include "rust-common.h" + +namespace Rust { +namespace AST { +// forward decls +class BlockExpr; +class TypePath; + +// TODO: inline? +/*struct AbiName { + std::string abi_name; + // Technically is meant to be STRING_LITERAL + + public: + // Returns whether abi name is empty, i.e. doesn't exist. + bool is_empty() const { + return abi_name.empty(); + } + + AbiName(std::string name) : abi_name(std::move(name)) {} + + // Empty AbiName constructor + AbiName() {} +};*/ + +// A type generic parameter (as opposed to a lifetime generic parameter) +class TypeParam : public GenericParam +{ + // bool has_outer_attribute; + // std::unique_ptr outer_attr; + Attribute outer_attr; + + Identifier type_representation; + + // bool has_type_param_bounds; + // TypeParamBounds type_param_bounds; + std::vector> + type_param_bounds; // inlined form + + // bool has_type; + std::unique_ptr type; + + Location locus; + +public: + Identifier get_type_representation () const { return type_representation; } + + // Returns whether the type of the type param has been specified. + bool has_type () const { return type != nullptr; } + + // Returns whether the type param has type param bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + // Returns whether the type param has an outer attribute. + bool has_outer_attribute () const { return !outer_attr.is_empty (); } + + TypeParam (Identifier type_representation, Location locus = Location (), + std::vector> type_param_bounds + = std::vector> (), + std::unique_ptr type = nullptr, + Attribute outer_attr = Attribute::create_empty ()) + : GenericParam (Analysis::Mappings::get ()->get_next_node_id ()), + outer_attr (std::move (outer_attr)), + type_representation (std::move (type_representation)), + type_param_bounds (std::move (type_param_bounds)), + type (std::move (type)), locus (locus) + {} + + // Copy constructor uses clone + TypeParam (TypeParam const &other) + : GenericParam (other.node_id), outer_attr (other.outer_attr), + type_representation (other.type_representation), locus (other.locus) + { + // guard to prevent null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overloaded assignment operator to clone + TypeParam &operator= (TypeParam const &other) + { + type_representation = other.type_representation; + outer_attr = other.outer_attr; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + TypeParam (TypeParam &&other) = default; + TypeParam &operator= (TypeParam &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + Kind get_kind () const override final { return Kind::Type; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + // TODO: mutable getter seems kinda dodgy + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + +protected: + // Clone function implementation as virtual method + TypeParam *clone_generic_param_impl () const override + { + return new TypeParam (*this); + } +}; + +/* "where" clause item base. Abstract - use LifetimeWhereClauseItem, + * TypeBoundWhereClauseItem */ +class WhereClauseItem +{ +public: + virtual ~WhereClauseItem () {} + + // Unique pointer custom clone function + std::unique_ptr clone_where_clause_item () const + { + return std::unique_ptr (clone_where_clause_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual NodeId get_node_id () const = 0; + +protected: + // Clone function implementation as pure virtual method + virtual WhereClauseItem *clone_where_clause_item_impl () const = 0; +}; + +// A lifetime where clause item +class LifetimeWhereClauseItem : public WhereClauseItem +{ + Lifetime lifetime; + std::vector lifetime_bounds; + Location locus; + NodeId node_id; + +public: + LifetimeWhereClauseItem (Lifetime lifetime, + std::vector lifetime_bounds, + Location locus) + : lifetime (std::move (lifetime)), + lifetime_bounds (std::move (lifetime_bounds)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + NodeId get_node_id () const override final { return node_id; } + + Lifetime &get_lifetime () { return lifetime; } + + std::vector &get_lifetime_bounds () { return lifetime_bounds; } + + Location get_locus () const { return locus; } + +protected: + // Clone function implementation as (not pure) virtual method + LifetimeWhereClauseItem *clone_where_clause_item_impl () const override + { + return new LifetimeWhereClauseItem (*this); + } +}; + +// A type bound where clause item +class TypeBoundWhereClauseItem : public WhereClauseItem +{ + std::vector for_lifetimes; + std::unique_ptr bound_type; + std::vector> type_param_bounds; + NodeId node_id; + Location locus; + +public: + // Returns whether the item has ForLifetimes + bool has_for_lifetimes () const { return !for_lifetimes.empty (); } + + // Returns whether the item has type param bounds + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + TypeBoundWhereClauseItem ( + std::vector for_lifetimes, std::unique_ptr bound_type, + std::vector> type_param_bounds, + Location locus) + : for_lifetimes (std::move (for_lifetimes)), + bound_type (std::move (bound_type)), + type_param_bounds (std::move (type_param_bounds)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor requires clone + TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other) + : for_lifetimes (other.for_lifetimes), + bound_type (other.bound_type->clone_type ()) + { + node_id = other.node_id; + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overload assignment operator to clone + TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other) + { + node_id = other.node_id; + for_lifetimes = other.for_lifetimes; + bound_type = other.bound_type->clone_type (); + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default; + TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + std::unique_ptr &get_type () + { + rust_assert (bound_type != nullptr); + return bound_type; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + NodeId get_node_id () const override final { return node_id; } + + Location get_locus () const { return locus; } + +protected: + // Clone function implementation as (not pure) virtual method + TypeBoundWhereClauseItem *clone_where_clause_item_impl () const override + { + return new TypeBoundWhereClauseItem (*this); + } +}; + +// A where clause +struct WhereClause +{ +private: + std::vector> where_clause_items; + NodeId node_id; + +public: + WhereClause (std::vector> where_clause_items) + : where_clause_items (std::move (where_clause_items)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // copy constructor with vector clone + WhereClause (WhereClause const &other) + { + node_id = other.node_id; + where_clause_items.reserve (other.where_clause_items.size ()); + for (const auto &e : other.where_clause_items) + where_clause_items.push_back (e->clone_where_clause_item ()); + } + + // overloaded assignment operator with vector clone + WhereClause &operator= (WhereClause const &other) + { + node_id = other.node_id; + where_clause_items.reserve (other.where_clause_items.size ()); + for (const auto &e : other.where_clause_items) + where_clause_items.push_back (e->clone_where_clause_item ()); + + return *this; + } + + // move constructors + WhereClause (WhereClause &&other) = default; + WhereClause &operator= (WhereClause &&other) = default; + + // Creates a WhereClause with no items. + static WhereClause create_empty () + { + return WhereClause (std::vector> ()); + } + + // Returns whether the WhereClause has no items. + bool is_empty () const { return where_clause_items.empty (); } + + std::string as_string () const; + + NodeId get_node_id () const { return node_id; } + + // TODO: this mutable getter seems kinda dodgy + std::vector> &get_items () + { + return where_clause_items; + } + const std::vector> &get_items () const + { + return where_clause_items; + } +}; + +// A self parameter in a method +struct SelfParam +{ +private: + bool has_ref; + bool is_mut; + // bool has_lifetime; // only possible if also ref + Lifetime lifetime; + + // bool has_type; // only possible if not ref + std::unique_ptr type; + + NodeId node_id; + + Location locus; + + // Unrestricted constructor used for error state + SelfParam (Lifetime lifetime, bool has_ref, bool is_mut, Type *type) + : has_ref (has_ref), is_mut (is_mut), lifetime (std::move (lifetime)), + type (type), node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + // this is ok as no outside classes can ever call this + + // TODO: self param can have outer attributes + +public: + // Returns whether the self-param has a type field. + bool has_type () const { return type != nullptr; } + + // Returns whether the self-param has a valid lifetime. + bool has_lifetime () const { return !lifetime.is_error (); } + + // Returns whether the self-param is in an error state. + bool is_error () const + { + return (has_type () && has_lifetime ()) || (has_lifetime () && !has_ref); + // not having either is not an error + } + + // Creates an error state self-param. + static SelfParam create_error () + { + // cannot have no ref but have a lifetime at the same time + return SelfParam (Lifetime (Lifetime::STATIC), false, false, nullptr); + } + + // Type-based self parameter (not ref, no lifetime) + SelfParam (std::unique_ptr type, bool is_mut, Location locus) + : has_ref (false), is_mut (is_mut), lifetime (Lifetime::error ()), + type (std::move (type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Lifetime-based self parameter (is ref, no type) + SelfParam (Lifetime lifetime, bool is_mut, Location locus) + : has_ref (true), is_mut (is_mut), lifetime (std::move (lifetime)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor requires clone + SelfParam (SelfParam const &other) + : has_ref (other.has_ref), is_mut (other.is_mut), lifetime (other.lifetime), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), + locus (other.locus) + { + node_id = other.node_id; + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overload assignment operator to use clone + SelfParam &operator= (SelfParam const &other) + { + is_mut = other.is_mut; + has_ref = other.has_ref; + lifetime = other.lifetime; + locus = other.locus; + node_id = other.node_id; + + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + SelfParam (SelfParam &&other) = default; + SelfParam &operator= (SelfParam &&other) = default; + + std::string as_string () const; + + Location get_locus () const { return locus; } + + bool get_has_ref () const { return has_ref; }; + bool get_is_mut () const { return is_mut; } + + Lifetime get_lifetime () const { return lifetime; } + + NodeId get_node_id () const { return node_id; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (has_type ()); + return type; + } +}; + +// Qualifiers for function, i.e. const, unsafe, extern etc. +struct FunctionQualifiers +{ +private: + AsyncConstStatus const_status; + bool has_unsafe; + bool has_extern; + std::string extern_abi; + Location locus; + +public: + FunctionQualifiers (Location locus, AsyncConstStatus const_status, + bool has_unsafe, bool has_extern = false, + std::string extern_abi = std::string ()) + : const_status (const_status), has_unsafe (has_unsafe), + has_extern (has_extern), extern_abi (std::move (extern_abi)), + locus (locus) + { + if (!this->extern_abi.empty ()) + { + // having extern is required; not having it is an implementation error + rust_assert (has_extern); + } + } + + std::string as_string () const; + + AsyncConstStatus get_const_status () const { return const_status; } + bool is_unsafe () const { return has_unsafe; } + bool is_extern () const { return has_extern; } + std::string get_extern_abi () const { return extern_abi; } + bool has_abi () const { return !extern_abi.empty (); } + + Location get_locus () const { return locus; } +}; + +// A function parameter +struct FunctionParam +{ +private: + std::vector outer_attrs; + Location locus; + std::unique_ptr param_name; + std::unique_ptr type; + +public: + FunctionParam (std::unique_ptr param_name, + std::unique_ptr param_type, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), locus (locus), + param_name (std::move (param_name)), type (std::move (param_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor uses clone + FunctionParam (FunctionParam const &other) + : locus (other.locus), node_id (other.node_id) + { + // guard to prevent nullptr dereference + if (other.param_name != nullptr) + param_name = other.param_name->clone_pattern (); + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overload assignment operator to use clone + FunctionParam &operator= (FunctionParam const &other) + { + locus = other.locus; + node_id = other.node_id; + + // guard to prevent nullptr dereference + if (other.param_name != nullptr) + param_name = other.param_name->clone_pattern (); + else + param_name = nullptr; + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + FunctionParam (FunctionParam &&other) = default; + FunctionParam &operator= (FunctionParam &&other) = default; + + // Returns whether FunctionParam is in an invalid state. + bool is_error () const { return param_name == nullptr || type == nullptr; } + + // Creates an error FunctionParam. + static FunctionParam create_error () + { + return FunctionParam (nullptr, nullptr, {}, Location ()); + } + + std::string as_string () const; + + Location get_locus () const { return locus; } + + // TODO: seems kinda dodgy. Think of better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_pattern () + { + rust_assert (param_name != nullptr); + return param_name; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + NodeId get_node_id () const { return node_id; } + +protected: + NodeId node_id; +}; + +// Visibility of item - if the item has it, then it is some form of public +struct Visibility +{ +public: + enum VisType + { + PRIV, + PUB, + PUB_CRATE, + PUB_SELF, + PUB_SUPER, + PUB_IN_PATH + }; + +private: + VisType vis_type; + // Only assigned if vis_type is IN_PATH + SimplePath in_path; + + // should this store location info? + +public: + // Creates a Visibility - TODO make constructor protected or private? + Visibility (VisType vis_type, SimplePath in_path) + : vis_type (vis_type), in_path (std::move (in_path)) + {} + + VisType get_public_vis_type () const { return vis_type; } + + // Returns whether visibility is in an error state. + bool is_error () const + { + return vis_type == PUB_IN_PATH && in_path.is_empty (); + } + + // Returns whether a visibility has a path + bool has_path () const { return !(is_error ()) && vis_type == PUB_IN_PATH; } + + // Returns whether visibility is public or not. + bool is_public () const { return vis_type != PRIV && !is_error (); } + + // Creates an error visibility. + static Visibility create_error () + { + return Visibility (PUB_IN_PATH, SimplePath::create_empty ()); + } + + // Unique pointer custom clone function + /*std::unique_ptr clone_visibility() const { + return std::unique_ptr(clone_visibility_impl()); + }*/ + + /* TODO: think of a way to only allow valid Visibility states - polymorphism + * is one idea but may be too resource-intensive. */ + + // Creates a public visibility with no further features/arguments. + static Visibility create_public () + { + return Visibility (PUB, SimplePath::create_empty ()); + } + + // Creates a public visibility with crate-relative paths + static Visibility create_crate (Location crate_tok_location) + { + return Visibility (PUB_CRATE, + SimplePath::from_str ("crate", crate_tok_location)); + } + + // Creates a public visibility with self-relative paths + static Visibility create_self (Location self_tok_location) + { + return Visibility (PUB_SELF, + SimplePath::from_str ("self", self_tok_location)); + } + + // Creates a public visibility with parent module-relative paths + static Visibility create_super (Location super_tok_location) + { + return Visibility (PUB_SUPER, + SimplePath::from_str ("super", super_tok_location)); + } + + // Creates a private visibility + static Visibility create_private () + { + return Visibility (PRIV, SimplePath::create_empty ()); + } + + // Creates a public visibility with a given path or whatever. + static Visibility create_in_path (SimplePath in_path) + { + return Visibility (PUB_IN_PATH, std::move (in_path)); + } + + std::string as_string () const; + const SimplePath &get_path () const { return in_path; } + SimplePath &get_path () { return in_path; } + +protected: + // Clone function implementation - not currently virtual but may be if + // polymorphism used + /*virtual*/ Visibility *clone_visibility_impl () const + { + return new Visibility (*this); + } +}; + +// A method (function belonging to a type) +class Method : public InherentImplItem, public TraitImplItem +{ + std::vector outer_attrs; + Visibility vis; + FunctionQualifiers qualifiers; + Identifier method_name; + std::vector> generic_params; + SelfParam self_param; + std::vector function_params; + std::unique_ptr return_type; + WhereClause where_clause; + std::unique_ptr function_body; + Location locus; + NodeId node_id; + +public: + // Returns whether the method is in an error state. + bool is_error () const + { + return function_body == nullptr || method_name.empty () + || self_param.is_error (); + } + + // Creates an error state method. + static Method create_error () + { + return Method ("", FunctionQualifiers (Location (), NONE, true), + std::vector> (), + SelfParam::create_error (), std::vector (), + nullptr, WhereClause::create_empty (), nullptr, + Visibility::create_error (), std::vector (), {}); + } + + // Returns whether the method has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether the method has parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether the method has a return type (void otherwise). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether the where clause exists (i.e. has items) + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether method has a non-default visibility. + bool has_visibility () const { return !vis.is_error (); } + + // Mega-constructor with all possible fields + Method (Identifier method_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + SelfParam self_param, std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause, + std::unique_ptr function_body, Visibility vis, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), vis (std::move (vis)), + qualifiers (std::move (qualifiers)), + method_name (std::move (method_name)), + generic_params (std::move (generic_params)), + self_param (std::move (self_param)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_body (std::move (function_body)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // TODO: add constructor with less fields + + // Copy constructor with clone + Method (Method const &other) + : outer_attrs (other.outer_attrs), vis (other.vis), + qualifiers (other.qualifiers), method_name (other.method_name), + self_param (other.self_param), function_params (other.function_params), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + node_id = other.node_id; + } + + // Overloaded assignment operator to clone + Method &operator= (Method const &other) + { + method_name = other.method_name; + outer_attrs = other.outer_attrs; + vis = other.vis; + qualifiers = other.qualifiers; + self_param = other.self_param; + function_params = other.function_params; + where_clause = other.where_clause; + locus = other.locus; + + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + else + function_body = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + node_id = other.node_id; + + return *this; + } + + // move constructors + Method (Method &&other) = default; + Method &operator= (Method &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { function_body = nullptr; } + bool is_marked_for_strip () const override + { + return function_body == nullptr; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () + { + rust_assert (function_body != nullptr); + return function_body; + } + + SelfParam &get_self_param () { return self_param; } + const SelfParam &get_self_param () const { return self_param; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_method_name () const { return method_name; } + + NodeId get_node_id () const { return node_id; } + + Location get_locus () const override final { return locus; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } + + const Visibility &get_visibility () const { return vis; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Method *clone_inherent_impl_item_impl () const final override + { + return clone_method_impl (); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Method *clone_trait_impl_item_impl () const final override + { + return clone_method_impl (); + } + + /*virtual*/ Method *clone_method_impl () const { return new Method (*this); } +}; + +// Item that supports visibility - abstract base class +class VisItem : public Item +{ + Visibility visibility; + std::vector outer_attrs; + +protected: + // Visibility constructor + VisItem (Visibility visibility, + std::vector outer_attrs = std::vector ()) + : visibility (std::move (visibility)), outer_attrs (std::move (outer_attrs)) + {} + + // Visibility copy constructor + VisItem (VisItem const &other) + : visibility (other.visibility), outer_attrs (other.outer_attrs) + {} + + // Overload assignment operator to clone + VisItem &operator= (VisItem const &other) + { + visibility = other.visibility; + outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + VisItem (VisItem &&other) = default; + VisItem &operator= (VisItem &&other) = default; + +public: + /* Does the item have some kind of public visibility (non-default + * visibility)? */ + bool has_visibility () const { return visibility.is_public (); } + + std::string as_string () const override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + Visibility &get_visibility () { return visibility; } + const Visibility &get_visibility () const { return visibility; } + + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } +}; + +// Rust module item - abstract base class +class Module : public VisItem +{ +public: + // Type of the current module. A module can be either loaded or unloaded, + // meaning that the items of the module can already be present or not. For + // example, the following module would be loaded: `mod foo { fn bar() {} }`. + // However, the module would be unloaded if it refers to an external file (i.e + // `mod foo;`) and then become loaded upon expansion. + enum ModuleKind + { + LOADED, + UNLOADED, + }; + + Identifier get_name () const { return module_name; } + +private: + Identifier module_name; + Location locus; + ModuleKind kind; + + // Name of the file including the module + std::string outer_filename; + // bool has_inner_attrs; + std::vector inner_attrs; + // bool has_items; + std::vector> items; + // Names of including inline modules (immediate parent is last in the list) + std::vector module_scope; + + // Filename the module refers to. Empty string on LOADED modules or if an + // error occured when dealing with UNLOADED modules + std::string module_file; + + void clone_items (const std::vector> &other_items) + { + items.reserve (other_items.size ()); + for (const auto &e : other_items) + items.push_back (e->clone_item ()); + } + +public: + // Returns whether the module has items in its body. + bool has_items () const { return !items.empty (); } + + // Returns whether the module has any inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + // Unloaded module constructor + Module (Identifier module_name, Visibility visibility, + std::vector outer_attrs, Location locus, + std::string outer_filename, std::vector module_scope) + : VisItem (std::move (visibility), std::move (outer_attrs)), + module_name (module_name), locus (locus), kind (ModuleKind::UNLOADED), + outer_filename (outer_filename), inner_attrs (std::vector ()), + items (std::vector> ()), + module_scope (std::move (module_scope)) + {} + + // Loaded module constructor, with items + Module (Identifier name, Location locus, + std::vector> items, + Visibility visibility = Visibility::create_error (), + std::vector inner_attrs = std::vector (), + std::vector outer_attrs = std::vector ()) + : VisItem (std::move (visibility), std::move (outer_attrs)), + module_name (name), locus (locus), kind (ModuleKind::LOADED), + outer_filename (std::string ()), inner_attrs (std::move (inner_attrs)), + items (std::move (items)) + {} + + // Copy constructor with vector clone + Module (Module const &other) + : VisItem (other), module_name (other.module_name), locus (other.locus), + kind (other.kind), inner_attrs (other.inner_attrs), + module_scope (other.module_scope) + { + // We need to check whether we are copying a loaded module or an unloaded + // one. In the second case, clear the `items` vector. + if (other.kind == LOADED) + clone_items (other.items); + else + items.clear (); + } + + // Overloaded assignment operator with vector clone + Module &operator= (Module const &other) + { + VisItem::operator= (other); + + module_name = other.module_name; + locus = other.locus; + kind = other.kind; + inner_attrs = other.inner_attrs; + module_scope = other.module_scope; + + // Likewise, we need to clear the `items` vector in case the other module is + // unloaded + if (kind == LOADED) + clone_items (other.items); + else + items.clear (); + + return *this; + } + + // Search for the filename associated with an external module, storing it in + // module_file + void process_file_path (); + // Load the items contained in an external module + void load_items (); + + void accept_vis (ASTVisitor &vis) override; + + /* Override that runs the function recursively on all items contained within + * the module. */ + void add_crate_name (std::vector &names) const override; + + // Returns the kind of the module + enum ModuleKind get_kind () const { return kind; } + + // TODO: think of better way to do this - mutable getter seems dodgy + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector> &get_items () const { return items; } + std::vector> &get_items () { return items; } + + // move constructors + Module (Module &&other) = default; + Module &operator= (Module &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { module_name = ""; } + bool is_marked_for_strip () const override { return module_name.empty (); } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Module *clone_item_impl () const override { return new Module (*this); } +}; + +// Rust extern crate declaration AST node +class ExternCrate : public VisItem +{ + // this is either an identifier or "self", with self parsed to string + std::string referenced_crate; + // bool has_as_clause; + // AsClause as_clause; + // this is either an identifier or "_", with _ parsed to string + std::string as_clause_name; + + Location locus; + + /* e.g. + "extern crate foo as _" + "extern crate foo" + "extern crate std as cool_std" */ +public: + std::string as_string () const override; + + // Returns whether extern crate declaration has an as clause. + bool has_as_clause () const { return !as_clause_name.empty (); } + + /* Returns whether extern crate declaration references the current crate + * (i.e. self). */ + bool references_self () const { return referenced_crate == "self"; } + + // Constructor + ExternCrate (std::string referenced_crate, Visibility visibility, + std::vector outer_attrs, Location locus, + std::string as_clause_name = std::string ()) + : VisItem (std::move (visibility), std::move (outer_attrs)), + referenced_crate (std::move (referenced_crate)), + as_clause_name (std::move (as_clause_name)), locus (locus) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + const std::string &get_referenced_crate () const { return referenced_crate; } + const std::string &get_as_clause () const { return as_clause_name; } + + // Override that adds extern crate name in decl to passed list of names. + void add_crate_name (std::vector &names) const override + { + names.push_back (referenced_crate); + } + + // Invalid if crate name is empty, so base stripping on that. + void mark_for_strip () override { referenced_crate = ""; } + bool is_marked_for_strip () const override + { + return referenced_crate.empty (); + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternCrate *clone_item_impl () const override + { + return new ExternCrate (*this); + } +}; + +// The path-ish thing referred to in a use declaration - abstract base class +class UseTree +{ + Location locus; + +public: + enum Kind + { + Glob, + Rebind, + List, + }; + + virtual ~UseTree () {} + + // Overload assignment operator to clone + UseTree &operator= (UseTree const &other) + { + locus = other.locus; + + return *this; + } + + UseTree (const UseTree &other) = default; + + // move constructors + UseTree (UseTree &&other) = default; + UseTree &operator= (UseTree &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_use_tree () const + { + return std::unique_ptr (clone_use_tree_impl ()); + } + + virtual std::string as_string () const = 0; + virtual Kind get_kind () const = 0; + + Location get_locus () const { return locus; } + + virtual void accept_vis (ASTVisitor &vis) = 0; + +protected: + // Clone function implementation as pure virtual method + virtual UseTree *clone_use_tree_impl () const = 0; + + UseTree (Location locus) : locus (locus) {} +}; + +// Use tree with a glob (wildcard) operator +class UseTreeGlob : public UseTree +{ +public: + enum PathType + { + NO_PATH, + GLOBAL, + PATH_PREFIXED + }; + +private: + PathType glob_type; + SimplePath path; + +public: + UseTreeGlob (PathType glob_type, SimplePath path, Location locus) + : UseTree (locus), glob_type (glob_type), path (std::move (path)) + { + if (this->glob_type != PATH_PREFIXED) + { + // compiler implementation error if there is a path with a + // non-path-prefixed use tree glob + rust_assert (!has_path ()); + } + // TODO: do path-prefixed paths also have to have a path? If so, have an + // assert for that too. + } + + /* Returns whether has path. Should be made redundant by PathType + * PATH_PREFIXED. */ + bool has_path () const { return !path.is_empty (); } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return Glob; } + + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + /* TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory + * methods? */ +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseTreeGlob *clone_use_tree_impl () const override + { + return new UseTreeGlob (*this); + } +}; + +// Use tree with a list of paths with a common prefix +class UseTreeList : public UseTree +{ +public: + enum PathType + { + NO_PATH, + GLOBAL, + PATH_PREFIXED + }; + +private: + PathType path_type; + SimplePath path; + + std::vector> trees; + +public: + UseTreeList (PathType path_type, SimplePath path, + std::vector> trees, Location locus) + : UseTree (locus), path_type (path_type), path (std::move (path)), + trees (std::move (trees)) + { + if (this->path_type != PATH_PREFIXED) + { + // compiler implementation error if there is a path with a + // non-path-prefixed use tree glob + rust_assert (!has_path ()); + } + // TODO: do path-prefixed paths also have to have a path? If so, have an + // assert for that too. + } + + // copy constructor with vector clone + UseTreeList (UseTreeList const &other) + : UseTree (other), path_type (other.path_type), path (other.path) + { + trees.reserve (other.trees.size ()); + for (const auto &e : other.trees) + trees.push_back (e->clone_use_tree ()); + } + + // overloaded assignment operator with vector clone + UseTreeList &operator= (UseTreeList const &other) + { + UseTree::operator= (other); + path_type = other.path_type; + path = other.path; + + trees.reserve (other.trees.size ()); + for (const auto &e : other.trees) + trees.push_back (e->clone_use_tree ()); + + return *this; + } + + // move constructors + UseTreeList (UseTreeList &&other) = default; + UseTreeList &operator= (UseTreeList &&other) = default; + + // Returns whether has path. Should be made redundant by path_type. + bool has_path () const { return !path.is_empty (); } + + // Returns whether has inner tree elements. + bool has_trees () const { return !trees.empty (); } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return List; } + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + const std::vector> &get_trees () const + { + return trees; + } + + // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory + // methods? +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseTreeList *clone_use_tree_impl () const override + { + return new UseTreeList (*this); + } +}; + +// Use tree where it rebinds the module name as something else +class UseTreeRebind : public UseTree +{ +public: + enum NewBindType + { + NONE, + IDENTIFIER, + WILDCARD + }; + +private: + SimplePath path; + + NewBindType bind_type; + Identifier identifier; // only if NewBindType is IDENTIFIER + +public: + UseTreeRebind (NewBindType bind_type, SimplePath path, Location locus, + Identifier identifier = std::string ()) + : UseTree (locus), path (std::move (path)), bind_type (bind_type), + identifier (std::move (identifier)) + {} + + // Returns whether has path (this should always be true). + bool has_path () const { return !path.is_empty (); } + + // Returns whether has identifier (or, rather, is allowed to). + bool has_identifier () const { return bind_type == IDENTIFIER; } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return Rebind; } + + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + const Identifier &get_identifier () const + { + rust_assert (has_identifier ()); + return identifier; + } + + // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory + // methods? +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + virtual UseTreeRebind *clone_use_tree_impl () const override + { + return new UseTreeRebind (*this); + } +}; + +// Rust use declaration (i.e. for modules) AST node +class UseDeclaration : public VisItem +{ + std::unique_ptr use_tree; + Location locus; + +public: + std::string as_string () const override; + + UseDeclaration (std::unique_ptr use_tree, Visibility visibility, + std::vector outer_attrs, Location locus) + : VisItem (std::move (visibility), std::move (outer_attrs)), + use_tree (std::move (use_tree)), locus (locus) + {} + + // Copy constructor with clone + UseDeclaration (UseDeclaration const &other) + : VisItem (other), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.use_tree != nullptr) + use_tree = other.use_tree->clone_use_tree (); + } + + // Overloaded assignment operator to clone + UseDeclaration &operator= (UseDeclaration const &other) + { + VisItem::operator= (other); + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.use_tree != nullptr) + use_tree = other.use_tree->clone_use_tree (); + else + use_tree = nullptr; + + return *this; + } + + // move constructors + UseDeclaration (UseDeclaration &&other) = default; + UseDeclaration &operator= (UseDeclaration &&other) = default; + + Location get_locus () const override final { return locus; } + const std::unique_ptr &get_tree () const { return use_tree; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if use tree is null, so base stripping on that. + void mark_for_strip () override { use_tree = nullptr; } + bool is_marked_for_strip () const override { return use_tree == nullptr; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseDeclaration *clone_item_impl () const override + { + return new UseDeclaration (*this); + } +}; + +class LetStmt; + +// Rust function declaration AST node +class Function : public VisItem, public InherentImplItem, public TraitImplItem +{ + FunctionQualifiers qualifiers; + Identifier function_name; + std::vector> generic_params; + std::vector function_params; + std::unique_ptr return_type; + WhereClause where_clause; + std::unique_ptr function_body; + Location locus; + +public: + std::string as_string () const override; + + // Returns whether function has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether function has regular parameters. + bool has_function_params () const { return !function_params.empty (); } + + // Returns whether function has return type - if not, it is void. + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether function has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Mega-constructor with all possible fields + Function (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause, + std::unique_ptr function_body, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_body (std::move (function_body)), locus (locus) + {} + + // TODO: add constructor with less fields + + // Copy constructor with clone + Function (Function const &other) + : VisItem (other), qualifiers (other.qualifiers), + function_name (other.function_name), + function_params (other.function_params), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator to clone + Function &operator= (Function const &other) + { + VisItem::operator= (other); + function_name = other.function_name; + qualifiers = other.qualifiers; + function_params = other.function_params; + where_clause = other.where_clause; + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + else + function_body = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Function (Function &&other) = default; + Function &operator= (Function &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { function_body = nullptr; } + bool is_marked_for_strip () const override + { + return function_body == nullptr; + } + + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () + { + rust_assert (function_body != nullptr); + return function_body; + } + + const FunctionQualifiers &get_qualifiers () const { return qualifiers; } + + Identifier get_function_name () const { return function_name; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_item_impl () const override { return new Function (*this); } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_inherent_impl_item_impl () const override + { + return new Function (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_trait_impl_item_impl () const override + { + return new Function (*this); + } +}; + +// Rust type alias (i.e. typedef) AST node +class TypeAlias : public VisItem, public TraitImplItem +{ + Identifier new_type_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::unique_ptr existing_type; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether type alias has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether type alias has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Mega-constructor with all possible fields + TypeAlias (Identifier new_type_name, + std::vector> generic_params, + WhereClause where_clause, std::unique_ptr existing_type, + Visibility vis, std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + new_type_name (std::move (new_type_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), + existing_type (std::move (existing_type)), locus (locus) + {} + + // Copy constructor + TypeAlias (TypeAlias const &other) + : VisItem (other), new_type_name (other.new_type_name), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.existing_type != nullptr) + existing_type = other.existing_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator to clone + TypeAlias &operator= (TypeAlias const &other) + { + VisItem::operator= (other); + new_type_name = other.new_type_name; + where_clause = other.where_clause; + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.existing_type != nullptr) + existing_type = other.existing_type->clone_type (); + else + existing_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TypeAlias (TypeAlias &&other) = default; + TypeAlias &operator= (TypeAlias &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if existing type is null, so base stripping on that. + void mark_for_strip () override { existing_type = nullptr; } + bool is_marked_for_strip () const override + { + return existing_type == nullptr; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type_aliased () + { + rust_assert (existing_type != nullptr); + return existing_type; + } + + Identifier get_new_type_name () const { return new_type_name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TypeAlias *clone_item_impl () const override { return new TypeAlias (*this); } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + TypeAlias *clone_trait_impl_item_impl () const override + { + return new TypeAlias (*this); + } +}; + +// Rust base struct declaration AST node - abstract base class +class Struct : public VisItem +{ +protected: + // protected to enable access by derived classes - allows better as_string + Identifier struct_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + +private: + Location locus; + +public: + // Returns whether struct has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether struct has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Location get_locus () const override final { return locus; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { struct_name = ""; } + bool is_marked_for_strip () const override { return struct_name.empty (); } + + Identifier get_struct_name () const { return struct_name; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_identifier () const { return struct_name; } + +protected: + Struct (Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : VisItem (std::move (vis), std::move (outer_attrs)), + struct_name (std::move (struct_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), locus (locus) + {} + + // Copy constructor with vector clone + Struct (Struct const &other) + : VisItem (other), struct_name (other.struct_name), + where_clause (other.where_clause), locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator with vector clone + Struct &operator= (Struct const &other) + { + VisItem::operator= (other); + struct_name = other.struct_name; + where_clause = other.where_clause; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Struct (Struct &&other) = default; + Struct &operator= (Struct &&other) = default; +}; + +// A single field in a struct +struct StructField +{ +private: + // bool has_outer_attributes; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier field_name; + std::unique_ptr field_type; + + NodeId node_id; + + Location locus; + +public: + // Returns whether struct field has any outer attributes. + bool has_outer_attributes () const { return !outer_attrs.empty (); } + + // Returns whether struct field has a non-private (non-default) visibility. + bool has_visibility () const { return !visibility.is_error (); } + + StructField (Identifier field_name, std::unique_ptr field_type, + Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + field_name (std::move (field_name)), field_type (std::move (field_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor + StructField (StructField const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + field_name (other.field_name), node_id (other.node_id), + locus (other.locus) + { + // guard to prevent null dereference + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + } + + ~StructField () = default; + + // Overloaded assignment operator to clone + StructField &operator= (StructField const &other) + { + field_name = other.field_name; + visibility = other.visibility; + outer_attrs = other.outer_attrs; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + else + field_type = nullptr; + + return *this; + } + + // move constructors + StructField (StructField &&other) = default; + StructField &operator= (StructField &&other) = default; + + // Returns whether struct field is in an error state. + bool is_error () const + { + return field_name.empty () && field_type == nullptr; + // this should really be an or since neither are allowed + } + + // Creates an error state struct field. + static StructField create_error () + { + return StructField (std::string (""), nullptr, Visibility::create_error (), + Location ()); + } + + std::string as_string () const; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + Identifier get_field_name () const { return field_name; } + + Location get_locus () const { return locus; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_field_type () + { + rust_assert (field_type != nullptr); + return field_type; + } + + const Visibility &get_visibility () const { return visibility; } + + NodeId get_node_id () const { return node_id; } +}; + +// Rust struct declaration with true struct type AST node +class StructStruct : public Struct +{ + std::vector fields; + bool is_unit; + +public: + std::string as_string () const override; + + // Mega-constructor with all possible fields + StructStruct (std::vector fields, Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, bool is_unit, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + fields (std::move (fields)), is_unit (is_unit) + {} + + // Unit struct constructor + StructStruct (Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + is_unit (true) + {} + + /* Returns whether the struct is a unit struct - struct defined without + * fields. This is important because it also means an implicit constant of its + * type is defined. */ + bool is_unit_struct () const { return is_unit; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_fields () { return fields; } + const std::vector &get_fields () const { return fields; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + StructStruct *clone_item_impl () const override + { + return new StructStruct (*this); + } +}; + +// A single field in a tuple +struct TupleField +{ +private: + // bool has_outer_attributes; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + std::unique_ptr field_type; + + NodeId node_id; + + Location locus; + +public: + // Returns whether tuple field has outer attributes. + bool has_outer_attributes () const { return !outer_attrs.empty (); } + + /* Returns whether tuple field has a non-default visibility (i.e. a public + * one) */ + bool has_visibility () const { return !visibility.is_error (); } + + // Complete constructor + TupleField (std::unique_ptr field_type, Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + field_type (std::move (field_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor with clone + TupleField (TupleField const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + node_id (other.node_id), locus (other.locus) + { + // guard to prevent null dereference (only required if error) + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + } + + ~TupleField () = default; + + // Overloaded assignment operator to clone + TupleField &operator= (TupleField const &other) + { + visibility = other.visibility; + outer_attrs = other.outer_attrs; + node_id = other.node_id; + locus = other.locus; + + // guard to prevent null dereference (only required if error) + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + else + field_type = nullptr; + + return *this; + } + + // move constructors + TupleField (TupleField &&other) = default; + TupleField &operator= (TupleField &&other) = default; + + // Returns whether tuple field is in an error state. + bool is_error () const { return field_type == nullptr; } + + // Creates an error state tuple field. + static TupleField create_error () + { + return TupleField (nullptr, Visibility::create_error (), Location ()); + } + + std::string as_string () const; + + NodeId get_node_id () const { return node_id; } + + const Visibility &get_visibility () const { return visibility; } + + Location get_locus () const { return locus; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_field_type () + { + rust_assert (field_type != nullptr); + return field_type; + } +}; + +// Rust tuple declared using struct keyword AST node +class TupleStruct : public Struct +{ + std::vector fields; + +public: + std::string as_string () const override; + + // Mega-constructor with all possible fields + TupleStruct (std::vector fields, Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + fields (std::move (fields)) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_fields () { return fields; } + const std::vector &get_fields () const { return fields; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TupleStruct *clone_item_impl () const override + { + return new TupleStruct (*this); + } +}; + +/* An item used in an "enum" tagged union - not abstract: base represents a + * name-only enum. EnumItems (variants) syntactically allow a Visibility + * annotation. */ +class EnumItem : public VisItem +{ + Identifier variant_name; + + Location locus; + +public: + virtual ~EnumItem () {} + + EnumItem (Identifier variant_name, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + variant_name (std::move (variant_name)), locus (locus) + {} + + // Unique pointer custom clone function + std::unique_ptr clone_enum_item () const + { + return std::unique_ptr (clone_item_impl ()); + } + + virtual std::string as_string () const; + + // not pure virtual as not abstract + virtual void accept_vis (ASTVisitor &vis); + + Location get_locus () const { return locus; } + + Identifier get_identifier () const { return variant_name; } + + // Based on idea that name is never empty. + void mark_for_strip () { variant_name = ""; } + bool is_marked_for_strip () const { return variant_name.empty (); } + +protected: + EnumItem *clone_item_impl () const override { return new EnumItem (*this); } +}; + +// A tuple item used in an "enum" tagged union +class EnumItemTuple : public EnumItem +{ + // bool has_tuple_fields; + std::vector tuple_fields; + +public: + // Returns whether tuple enum item has tuple fields. + bool has_tuple_fields () const { return !tuple_fields.empty (); } + + EnumItemTuple (Identifier variant_name, Visibility vis, + std::vector tuple_fields, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + tuple_fields (std::move (tuple_fields)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_tuple_fields () { return tuple_fields; } + const std::vector &get_tuple_fields () const + { + return tuple_fields; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemTuple *clone_item_impl () const override + { + return new EnumItemTuple (*this); + } +}; + +// A struct item used in an "enum" tagged union +class EnumItemStruct : public EnumItem +{ + // bool has_struct_fields; + std::vector struct_fields; + +public: + // Returns whether struct enum item has struct fields. + bool has_struct_fields () const { return !struct_fields.empty (); } + + EnumItemStruct (Identifier variant_name, Visibility vis, + std::vector struct_fields, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + struct_fields (std::move (struct_fields)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_struct_fields () { return struct_fields; } + const std::vector &get_struct_fields () const + { + return struct_fields; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemStruct *clone_item_impl () const override + { + return new EnumItemStruct (*this); + } +}; + +// A discriminant (numbered enum) item used in an "enum" tagged union +class EnumItemDiscriminant : public EnumItem +{ + std::unique_ptr expression; + +public: + EnumItemDiscriminant (Identifier variant_name, Visibility vis, + std::unique_ptr expr, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + expression (std::move (expr)) + {} + + // Copy constructor with clone + EnumItemDiscriminant (EnumItemDiscriminant const &other) + : EnumItem (other), expression (other.expression->clone_expr ()) + {} + + // Overloaded assignment operator to clone + EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other) + { + EnumItem::operator= (other); + expression = other.expression->clone_expr (); + // variant_name = other.variant_name; + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + EnumItemDiscriminant (EnumItemDiscriminant &&other) = default; + EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expression != nullptr); + return expression; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemDiscriminant *clone_item_impl () const override + { + return new EnumItemDiscriminant (*this); + } +}; + +// AST node for Rust "enum" - tagged union +class Enum : public VisItem +{ + Identifier enum_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector> items; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether "enum" has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether "enum" has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + /* Returns whether enum is a "zero-variant" (no possible variant) enum, + * which cannot be instantiated. */ + bool is_zero_variant () const { return items.empty (); } + + // Mega-constructor + Enum (Identifier enum_name, Visibility vis, + std::vector> generic_params, + WhereClause where_clause, std::vector> items, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + enum_name (std::move (enum_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), items (std::move (items)), + locus (locus) + {} + + // TODO: constructor with less arguments + + // Copy constructor with vector clone + Enum (Enum const &other) + : VisItem (other), enum_name (other.enum_name), + where_clause (other.where_clause), locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_enum_item ()); + } + + // Overloaded assignment operator with vector clone + Enum &operator= (Enum const &other) + { + VisItem::operator= (other); + enum_name = other.enum_name; + where_clause = other.where_clause; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_enum_item ()); + + return *this; + } + + // Move constructors + Enum (Enum &&other) = default; + Enum &operator= (Enum &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + Identifier get_identifier () const { return enum_name; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { enum_name = ""; } + bool is_marked_for_strip () const override { return enum_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector> &get_variants () { return items; } + const std::vector> &get_variants () const + { + return items; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Enum *clone_item_impl () const override { return new Enum (*this); } +}; + +// Rust untagged union used for C compat AST node +class Union : public VisItem +{ + Identifier union_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector variants; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether union has generic params. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether union has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Union (Identifier union_name, Visibility vis, + std::vector> generic_params, + WhereClause where_clause, std::vector variants, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + union_name (std::move (union_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), variants (std::move (variants)), + locus (locus) + {} + + // copy constructor with vector clone + Union (Union const &other) + : VisItem (other), union_name (other.union_name), + where_clause (other.where_clause), variants (other.variants), + locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // overloaded assignment operator with vector clone + Union &operator= (Union const &other) + { + VisItem::operator= (other); + union_name = other.union_name; + where_clause = other.where_clause; + variants = other.variants; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Union (Union &&other) = default; + Union &operator= (Union &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { union_name = ""; } + bool is_marked_for_strip () const override { return union_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_variants () { return variants; } + const std::vector &get_variants () const { return variants; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_identifier () const { return union_name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Union *clone_item_impl () const override { return new Union (*this); } +}; + +/* "Constant item" AST node - used for constant, compile-time expressions + * within module scope (like constexpr) */ +class ConstantItem : public VisItem, + public InherentImplItem, + public TraitImplItem +{ + // either has an identifier or "_" - maybe handle in identifier? + // bool identifier_is_underscore; + // if no identifier declared, identifier will be "_" + std::string identifier; + + std::unique_ptr type; + std::unique_ptr const_expr; + + Location locus; + +public: + std::string as_string () const override; + + ConstantItem (std::string ident, Visibility vis, std::unique_ptr type, + std::unique_ptr const_expr, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + identifier (std::move (ident)), type (std::move (type)), + const_expr (std::move (const_expr)), locus (locus) + {} + + ConstantItem (ConstantItem const &other) + : VisItem (other), identifier (other.identifier), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + if (other.const_expr != nullptr) + const_expr = other.const_expr->clone_expr (); + } + + // Overload assignment operator to clone + ConstantItem &operator= (ConstantItem const &other) + { + VisItem::operator= (other); + identifier = other.identifier; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + if (other.const_expr != nullptr) + const_expr = other.const_expr->clone_expr (); + else + const_expr = nullptr; + + return *this; + } + + // move constructors + ConstantItem (ConstantItem &&other) = default; + ConstantItem &operator= (ConstantItem &&other) = default; + + /* Returns whether constant item is an "unnamed" (wildcard underscore used + * as identifier) constant. */ + bool is_unnamed () const { return identifier == "_"; } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type or expression are null, so base stripping on that. + void mark_for_strip () override + { + type = nullptr; + const_expr = nullptr; + } + bool is_marked_for_strip () const override + { + return type == nullptr && const_expr == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (const_expr != nullptr); + return const_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + std::string get_identifier () const { return identifier; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_item_impl () const override + { + return new ConstantItem (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_inherent_impl_item_impl () const override + { + return new ConstantItem (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_trait_impl_item_impl () const override + { + return new ConstantItem (*this); + } +}; + +/* Static item AST node - items within module scope with fixed storage + * duration? */ +class StaticItem : public VisItem +{ + bool has_mut; + Identifier name; + std::unique_ptr type; + std::unique_ptr expr; + Location locus; + +public: + std::string as_string () const override; + + StaticItem (Identifier name, bool is_mut, std::unique_ptr type, + std::unique_ptr expr, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), has_mut (is_mut), + name (std::move (name)), type (std::move (type)), expr (std::move (expr)), + locus (locus) + {} + + // Copy constructor with clone + StaticItem (StaticItem const &other) + : VisItem (other), has_mut (other.has_mut), name (other.name), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + } + + // Overloaded assignment operator to clone + StaticItem &operator= (StaticItem const &other) + { + VisItem::operator= (other); + name = other.name; + has_mut = other.has_mut; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + else + expr = nullptr; + + return *this; + } + + // move constructors + StaticItem (StaticItem &&other) = default; + StaticItem &operator= (StaticItem &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type or expression are null, so base stripping on that. + void mark_for_strip () override + { + type = nullptr; + expr = nullptr; + } + bool is_marked_for_strip () const override + { + return type == nullptr && expr == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + bool is_mutable () const { return has_mut; } + + Identifier get_identifier () const { return name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + StaticItem *clone_item_impl () const override + { + return new StaticItem (*this); + } +}; + +// Function declaration in traits +struct TraitFunctionDecl +{ +private: + // TODO: delete and replace with Function decl item? no as no body in this. + FunctionQualifiers qualifiers; + Identifier function_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_params; + // FunctionParams function_params; + std::vector function_params; // inlined + + // bool has_return_type; + std::unique_ptr return_type; + + // bool has_where_clause; + WhereClause where_clause; + + // should this store location info? + +public: + // Returns whether function decl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether function decl has regular parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether function has return type (otherwise is void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether function has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Identifier get_identifier () const { return function_name; } + + // Mega-constructor + TraitFunctionDecl (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + std::vector function_params, + std::unique_ptr return_type, + WhereClause where_clause) + : qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)) + {} + + // Copy constructor with clone + TraitFunctionDecl (TraitFunctionDecl const &other) + : qualifiers (other.qualifiers), function_name (other.function_name), + function_params (other.function_params), where_clause (other.where_clause) + { + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + ~TraitFunctionDecl () = default; + + // Overloaded assignment operator with clone + TraitFunctionDecl &operator= (TraitFunctionDecl const &other) + { + function_name = other.function_name; + qualifiers = other.qualifiers; + function_params = other.function_params; + where_clause = other.where_clause; + + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TraitFunctionDecl (TraitFunctionDecl &&other) = default; + TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default; + + std::string as_string () const; + + // Invalid if function name is empty, so base stripping on that. + void mark_for_strip () { function_name = ""; } + bool is_marked_for_strip () const { return function_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () { return return_type; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } +}; + +// Actual trait item function declaration within traits +class TraitItemFunc : public TraitItem +{ + std::vector outer_attrs; + TraitFunctionDecl decl; + std::unique_ptr block_expr; + Location locus; + +public: + // Returns whether function has a definition or is just a declaration. + bool has_definition () const { return block_expr != nullptr; } + + TraitItemFunc (TraitFunctionDecl decl, std::unique_ptr block_expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + decl (std::move (decl)), block_expr (std::move (block_expr)), + locus (locus) + {} + + // Copy constructor with clone + TraitItemFunc (TraitItemFunc const &other) + : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + } + + // Overloaded assignment operator to clone + TraitItemFunc &operator= (TraitItemFunc const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + decl = other.decl; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + else + block_expr = nullptr; + + return *this; + } + + // move constructors + TraitItemFunc (TraitItemFunc &&other) = default; + TraitItemFunc &operator= (TraitItemFunc &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait decl is empty, so base stripping on that. + void mark_for_strip () override { decl.mark_for_strip (); } + bool is_marked_for_strip () const override + { + return decl.is_marked_for_strip (); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () { return block_expr; } + + // TODO: is this better? Or is a "vis_block" better? + TraitFunctionDecl &get_trait_function_decl () + { + // TODO: maybe only allow access if not marked for strip? + return decl; + } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemFunc *clone_trait_item_impl () const override + { + return new TraitItemFunc (*this); + } +}; + +// Method declaration within traits +struct TraitMethodDecl +{ +private: + // TODO: delete and replace with Function decl item? no as no body. + FunctionQualifiers qualifiers; + Identifier function_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + SelfParam self_param; + + // bool has_params; + // FunctionParams function_params; + std::vector function_params; // inlined + + // bool has_return_type; + std::unique_ptr return_type; + + // bool has_where_clause; + WhereClause where_clause; + + // should this store location info? + +public: + // Returns whether method decl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether method decl has regular parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether method has return type (otherwise is void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether method has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Identifier get_identifier () const { return function_name; } + + // Mega-constructor + TraitMethodDecl (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + SelfParam self_param, + std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause) + : qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + self_param (std::move (self_param)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)) + {} + + // Copy constructor with clone + TraitMethodDecl (TraitMethodDecl const &other) + : qualifiers (other.qualifiers), function_name (other.function_name), + self_param (other.self_param), function_params (other.function_params), + where_clause (other.where_clause) + { + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + ~TraitMethodDecl () = default; + + // Overloaded assignment operator with clone + TraitMethodDecl &operator= (TraitMethodDecl const &other) + { + function_name = other.function_name; + qualifiers = other.qualifiers; + self_param = other.self_param; + function_params = other.function_params; + where_clause = other.where_clause; + + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TraitMethodDecl (TraitMethodDecl &&other) = default; + TraitMethodDecl &operator= (TraitMethodDecl &&other) = default; + + std::string as_string () const; + + // Invalid if method name is empty, so base stripping on that. + void mark_for_strip () { function_name = ""; } + bool is_marked_for_strip () const { return function_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () { return return_type; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + SelfParam &get_self_param () { return self_param; } + const SelfParam &get_self_param () const { return self_param; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } +}; + +// Actual trait item method declaration within traits +class TraitItemMethod : public TraitItem +{ + std::vector outer_attrs; + TraitMethodDecl decl; + std::unique_ptr block_expr; + Location locus; + +public: + // Returns whether method has a definition or is just a declaration. + bool has_definition () const { return block_expr != nullptr; } + + TraitItemMethod (TraitMethodDecl decl, std::unique_ptr block_expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + decl (std::move (decl)), block_expr (std::move (block_expr)), + locus (locus) + {} + + // Copy constructor with clone + TraitItemMethod (TraitItemMethod const &other) + : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + } + + // Overloaded assignment operator to clone + TraitItemMethod &operator= (TraitItemMethod const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + decl = other.decl; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + else + block_expr = nullptr; + + return *this; + } + + // move constructors + TraitItemMethod (TraitItemMethod &&other) = default; + TraitItemMethod &operator= (TraitItemMethod &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait decl is empty, so base stripping on that. + void mark_for_strip () override { decl.mark_for_strip (); } + bool is_marked_for_strip () const override + { + return decl.is_marked_for_strip (); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + TraitMethodDecl &get_trait_method_decl () + { + // TODO: maybe only allow access if not marked for strip? + return decl; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () { return block_expr; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemMethod *clone_trait_item_impl () const override + { + return new TraitItemMethod (*this); + } +}; + +// Constant item within traits +class TraitItemConst : public TraitItem +{ + std::vector outer_attrs; + Identifier name; + std::unique_ptr type; + + // bool has_expression; + std::unique_ptr expr; + + Location locus; + +public: + // Whether the constant item has an associated expression. + bool has_expression () const { return expr != nullptr; } + + TraitItemConst (Identifier name, std::unique_ptr type, + std::unique_ptr expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + name (std::move (name)), type (std::move (type)), expr (std::move (expr)), + locus (locus) + {} + + // Copy constructor with clones + TraitItemConst (TraitItemConst const &other) + : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + + // guard to prevent null dereference (only for error state) + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overloaded assignment operator to clone + TraitItemConst &operator= (TraitItemConst const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + name = other.name; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + else + expr = nullptr; + + // guard to prevent null dereference (only for error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + TraitItemConst (TraitItemConst &&other) = default; + TraitItemConst &operator= (TraitItemConst &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type is null, so base stripping on that. + void mark_for_strip () override { type = nullptr; } + bool is_marked_for_strip () const override { return type == nullptr; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + bool has_expr () const { return expr != nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (has_expr ()); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + Identifier get_identifier () const { return name; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemConst *clone_trait_item_impl () const override + { + return new TraitItemConst (*this); + } +}; + +// Type items within traits +class TraitItemType : public TraitItem +{ + std::vector outer_attrs; + + Identifier name; + + // bool has_type_param_bounds; + // TypeParamBounds type_param_bounds; + std::vector> + type_param_bounds; // inlined form + + Location locus; + +public: + // Returns whether trait item type has type param bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + TraitItemType (Identifier name, + std::vector> type_param_bounds, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + name (std::move (name)), + type_param_bounds (std::move (type_param_bounds)), locus (locus) + {} + + // Copy constructor with vector clone + TraitItemType (TraitItemType const &other) + : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus) + { + node_id = other.node_id; + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overloaded assignment operator with vector clone + TraitItemType &operator= (TraitItemType const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + name = other.name; + locus = other.locus; + node_id = other.node_id; + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // default move constructors + TraitItemType (TraitItemType &&other) = default; + TraitItemType &operator= (TraitItemType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { name = ""; } + bool is_marked_for_strip () const override { return name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: mutable getter seems kinda dodgy + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + Identifier get_identifier () const { return name; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemType *clone_trait_item_impl () const override + { + return new TraitItemType (*this); + } +}; + +// Rust trait item declaration AST node +class Trait : public VisItem +{ + bool has_unsafe; + Identifier name; + std::vector> generic_params; + std::vector> type_param_bounds; + WhereClause where_clause; + std::vector inner_attrs; + std::vector> trait_items; + Location locus; + +public: + std::string as_string () const override; + + // Returns whether trait has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether trait has type parameter bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + // Returns whether trait has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether trait has trait items. + bool has_trait_items () const { return !trait_items.empty (); } + + // Returns whether trait has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + Identifier get_identifier () const { return name; } + + bool is_unsafe () const { return has_unsafe; } + + // Mega-constructor + Trait (Identifier name, bool is_unsafe, + std::vector> generic_params, + std::vector> type_param_bounds, + WhereClause where_clause, + std::vector> trait_items, Visibility vis, + std::vector outer_attrs, std::vector inner_attrs, + Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + has_unsafe (is_unsafe), name (std::move (name)), + generic_params (std::move (generic_params)), + type_param_bounds (std::move (type_param_bounds)), + where_clause (std::move (where_clause)), + inner_attrs (std::move (inner_attrs)), + trait_items (std::move (trait_items)), locus (locus) + {} + + // Copy constructor with vector clone + Trait (Trait const &other) + : VisItem (other), has_unsafe (other.has_unsafe), name (other.name), + where_clause (other.where_clause), inner_attrs (other.inner_attrs), + locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + trait_items.reserve (other.trait_items.size ()); + for (const auto &e : other.trait_items) + trait_items.push_back (e->clone_trait_item ()); + } + + // Overloaded assignment operator with vector clone + Trait &operator= (Trait const &other) + { + VisItem::operator= (other); + name = other.name; + has_unsafe = other.has_unsafe; + where_clause = other.where_clause; + inner_attrs = other.inner_attrs; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + trait_items.reserve (other.trait_items.size ()); + for (const auto &e : other.trait_items) + trait_items.push_back (e->clone_trait_item ()); + + return *this; + } + + // default move constructors + Trait (Trait &&other) = default; + Trait &operator= (Trait &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait name is empty, so base stripping on that. + void mark_for_strip () override { name = ""; } + bool is_marked_for_strip () const override { return name.empty (); } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector> &get_trait_items () const + { + return trait_items; + } + std::vector> &get_trait_items () + { + return trait_items; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + WhereClause &get_where_clause () { return where_clause; } + + void insert_implict_self (std::unique_ptr &¶m) + { + std::vector> new_list; + new_list.reserve (generic_params.size () + 1); + + new_list.push_back (std::move (param)); + for (auto &p : generic_params) + { + new_list.push_back (std::move (p)); + } + + generic_params = std::move (new_list); + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Trait *clone_item_impl () const override { return new Trait (*this); } +}; + +// Implementation item declaration AST node - abstract base class +class Impl : public VisItem +{ + // must be protected to allow subclasses to access them properly +protected: + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + std::unique_ptr trait_type; + + // bool has_where_clause; + WhereClause where_clause; + + // bool has_inner_attrs; + std::vector inner_attrs; + +private: + // doesn't really need to be protected as write access probably not needed + Location locus; + +public: + // Returns whether impl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether impl has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether impl has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + Location get_locus () const override final { return locus; } + + // Invalid if trait type is null, so base stripping on that. + void mark_for_strip () override { trait_type = nullptr; } + bool is_marked_for_strip () const override { return trait_type == nullptr; } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (trait_type != nullptr); + return trait_type; + } + +protected: + // Mega-constructor + Impl (std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + generic_params (std::move (generic_params)), + trait_type (std::move (trait_type)), + where_clause (std::move (where_clause)), + inner_attrs (std::move (inner_attrs)), locus (locus) + {} + + // Copy constructor + Impl (Impl const &other) + : VisItem (other), where_clause (other.where_clause), + inner_attrs (other.inner_attrs), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.trait_type != nullptr) + trait_type = other.trait_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Assignment operator overload with cloning + Impl &operator= (Impl const &other) + { + VisItem::operator= (other); + where_clause = other.where_clause; + inner_attrs = other.inner_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.trait_type != nullptr) + trait_type = other.trait_type->clone_type (); + else + trait_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Impl (Impl &&other) = default; + Impl &operator= (Impl &&other) = default; +}; + +// Regular "impl foo" impl block declaration AST node +class InherentImpl : public Impl +{ + // bool has_impl_items; + std::vector> impl_items; + +public: + std::string as_string () const override; + + // Returns whether inherent impl block has inherent impl items. + bool has_impl_items () const { return !impl_items.empty (); } + + // Mega-constructor + InherentImpl (std::vector> impl_items, + std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : Impl (std::move (generic_params), std::move (trait_type), + std::move (where_clause), std::move (vis), std::move (inner_attrs), + std::move (outer_attrs), locus), + impl_items (std::move (impl_items)) + {} + + // Copy constructor with vector clone + InherentImpl (InherentImpl const &other) : Impl (other) + { + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_inherent_impl_item ()); + } + + // Overloaded assignment operator with vector clone + InherentImpl &operator= (InherentImpl const &other) + { + Impl::operator= (other); + + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_inherent_impl_item ()); + + return *this; + } + + // default move constructors + InherentImpl (InherentImpl &&other) = default; + InherentImpl &operator= (InherentImpl &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: think of better way to do this + const std::vector> &get_impl_items () const + { + return impl_items; + } + std::vector> &get_impl_items () + { + return impl_items; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + InherentImpl *clone_item_impl () const override + { + return new InherentImpl (*this); + } +}; + +// The "impl footrait for foo" impl block declaration AST node +class TraitImpl : public Impl +{ + bool has_unsafe; + bool has_exclam; + TypePath trait_path; + + // bool has_impl_items; + std::vector> impl_items; + +public: + std::string as_string () const override; + + // Returns whether trait impl has impl items. + bool has_impl_items () const { return !impl_items.empty (); } + + // Mega-constructor + TraitImpl (TypePath trait_path, bool is_unsafe, bool has_exclam, + std::vector> impl_items, + std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : Impl (std::move (generic_params), std::move (trait_type), + std::move (where_clause), std::move (vis), std::move (inner_attrs), + std::move (outer_attrs), locus), + has_unsafe (is_unsafe), has_exclam (has_exclam), + trait_path (std::move (trait_path)), impl_items (std::move (impl_items)) + {} + + // Copy constructor with vector clone + TraitImpl (TraitImpl const &other) + : Impl (other), has_unsafe (other.has_unsafe), + has_exclam (other.has_exclam), trait_path (other.trait_path) + { + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_trait_impl_item ()); + } + + // Overloaded assignment operator with vector clone + TraitImpl &operator= (TraitImpl const &other) + { + Impl::operator= (other); + trait_path = other.trait_path; + has_unsafe = other.has_unsafe; + has_exclam = other.has_exclam; + + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_trait_impl_item ()); + + return *this; + } + + // move constructors + TraitImpl (TraitImpl &&other) = default; + TraitImpl &operator= (TraitImpl &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + bool is_unsafe () const { return has_unsafe; }; + bool is_exclam () const { return has_exclam; } + + // TODO: think of better way to do this + const std::vector> &get_impl_items () const + { + return impl_items; + } + std::vector> &get_impl_items () + { + return impl_items; + } + + // TODO: is this better? Or is a "vis_block" better? + TypePath &get_trait_path () + { + // TODO: assert that trait path is not empty? + return trait_path; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TraitImpl *clone_item_impl () const override { return new TraitImpl (*this); } +}; + +#if 0 +// Abstract base class for an item used inside an extern block +class ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + +public: + virtual ~ExternalItem () {} + + /* TODO: spec syntax rules state that "MacroInvocationSemi" can be used as + * ExternalItem, but text body isn't so clear. Adding MacroInvocationSemi + * support would require a lot of refactoring. */ + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + // Unique pointer custom clone function + std::unique_ptr clone_external_item () const + { + return std::unique_ptr (clone_external_item_impl ()); + } + + virtual std::string as_string () const; + + Location get_locus () const override final { return locus; } + + virtual void accept_vis (ASTVisitor &vis) = 0; + + // TODO: make virtual? Would be more flexible. + // Based on idea that name should never be empty. + void mark_for_strip () { item_name = ""; }; + bool is_marked_for_strip () const { return item_name.empty (); }; + +protected: + ExternalItem (Identifier item_name, Visibility vis, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + item_name (std::move (item_name)), locus (locus) + {} + + // Copy constructor + ExternalItem (ExternalItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus) + {} + + // Overloaded assignment operator to clone + ExternalItem &operator= (ExternalItem const &other) + { + item_name = other.item_name; + visibility = other.visibility; + outer_attrs = other.outer_attrs; + locus = other.locus; + + return *this; + } + + // move constructors + ExternalItem (ExternalItem &&other) = default; + ExternalItem &operator= (ExternalItem &&other) = default; + + // Clone function implementation as pure virtual method + virtual ExternalItem *clone_external_item_impl () const = 0; + + // possibly make this public if required + std::string get_item_name () const { return item_name; } +}; +#endif + +// A static item used in an extern block +class ExternalStaticItem : public ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + + bool has_mut; + std::unique_ptr item_type; + +public: + ExternalStaticItem (Identifier item_name, std::unique_ptr item_type, + bool is_mut, Visibility vis, + std::vector outer_attrs, Location locus) + : ExternalItem (), outer_attrs (std::move (outer_attrs)), + visibility (std::move (vis)), item_name (std::move (item_name)), + locus (locus), has_mut (is_mut), item_type (std::move (item_type)) + {} + + // Copy constructor + ExternalStaticItem (ExternalStaticItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus), has_mut (other.has_mut) + { + node_id = other.node_id; + // guard to prevent null dereference (only required if error state) + if (other.item_type != nullptr) + item_type = other.item_type->clone_type (); + } + + // Overloaded assignment operator to clone + ExternalStaticItem &operator= (ExternalStaticItem const &other) + { + node_id = other.node_id; + outer_attrs = other.outer_attrs; + visibility = other.visibility; + item_name = other.item_name; + locus = other.locus; + has_mut = other.has_mut; + + // guard to prevent null dereference (only required if error state) + if (other.item_type != nullptr) + item_type = other.item_type->clone_type (); + else + item_type = nullptr; + + return *this; + } + + // move constructors + ExternalStaticItem (ExternalStaticItem &&other) = default; + ExternalStaticItem &operator= (ExternalStaticItem &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + Location get_locus () const { return locus; } + + // Based on idea that type should never be null. + void mark_for_strip () override { item_type = nullptr; }; + bool is_marked_for_strip () const override { return item_type == nullptr; }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (item_type != nullptr); + return item_type; + } + + Identifier get_identifier () const { return item_name; } + + const Visibility &get_visibility () const { return visibility; } + + bool is_mut () const { return has_mut; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternalStaticItem *clone_external_item_impl () const override + { + return new ExternalStaticItem (*this); + } +}; + +// A named function parameter used in external functions +struct NamedFunctionParam +{ +private: + // bool has_name; // otherwise is _ + std::string name; + + std::unique_ptr param_type; + + // seemingly new since writing this node + std::vector outer_attrs; + + NodeId node_id; + Location locus; + +public: + /* Returns whether the named function parameter has a name (i.e. name is not + * '_'). */ + bool has_name () const { return name != "_"; } + + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether the named function parameter is in an error state. + bool is_error () const + { + // also if identifier is "" but that is probably more costly to compute + return param_type == nullptr; + } + + std::string get_name () const { return name; } + + // Creates an error state named function parameter. + static NamedFunctionParam create_error () + { + return NamedFunctionParam ("", nullptr, {}, Location ()); + } + + NamedFunctionParam (std::string name, std::unique_ptr param_type, + std::vector outer_attrs, Location locus) + : name (std::move (name)), param_type (std::move (param_type)), + outer_attrs (std::move (outer_attrs)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor + NamedFunctionParam (NamedFunctionParam const &other) + : name (other.name), outer_attrs (other.outer_attrs) + { + node_id = other.node_id; + // guard to prevent null dereference (only required if error state) + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + } + + ~NamedFunctionParam () = default; + + // Overloaded assignment operator to clone + NamedFunctionParam &operator= (NamedFunctionParam const &other) + { + node_id = other.node_id; + name = other.name; + // has_name = other.has_name; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + else + param_type = nullptr; + + return *this; + } + + // move constructors + NamedFunctionParam (NamedFunctionParam &&other) = default; + NamedFunctionParam &operator= (NamedFunctionParam &&other) = default; + + std::string as_string () const; + + // Based on idea that nane should never be empty. + void mark_for_strip () { param_type = nullptr; }; + bool is_marked_for_strip () const { return is_error (); }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (param_type != nullptr); + return param_type; + } + + NodeId get_node_id () const { return node_id; } +}; + +// A function item used in an extern block +class ExternalFunctionItem : public ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_return_type; + // FunctionReturnType return_type; + std::unique_ptr return_type; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector function_params; + bool has_variadics; + std::vector variadic_outer_attrs; + +public: + // Returns whether item has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether item has a return type (otherwise void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether item has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + // Returns whether item has variadic parameters. + bool is_variadic () const { return has_variadics; } + + // Returns whether item has outer attributes on its variadic parameters. + bool has_variadic_outer_attrs () const + { + return !variadic_outer_attrs.empty (); + } + + Location get_locus () const { return locus; } + + const Visibility &get_visibility () const { return visibility; } + + ExternalFunctionItem ( + Identifier item_name, + std::vector> generic_params, + std::unique_ptr return_type, WhereClause where_clause, + std::vector function_params, bool has_variadics, + std::vector variadic_outer_attrs, Visibility vis, + std::vector outer_attrs, Location locus) + : ExternalItem (), outer_attrs (std::move (outer_attrs)), + visibility (std::move (vis)), item_name (std::move (item_name)), + locus (locus), generic_params (std::move (generic_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_params (std::move (function_params)), + has_variadics (has_variadics), + variadic_outer_attrs (std::move (variadic_outer_attrs)) + { + // TODO: assert that if has variadic outer attrs, then has_variadics is + // true? + } + + // Copy constructor with clone + ExternalFunctionItem (ExternalFunctionItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus), + where_clause (other.where_clause), + function_params (other.function_params), + has_variadics (other.has_variadics), + variadic_outer_attrs (other.variadic_outer_attrs) + { + node_id = other.node_id; + // guard to prevent null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator with clone + ExternalFunctionItem &operator= (ExternalFunctionItem const &other) + { + outer_attrs = other.outer_attrs; + visibility = other.visibility; + item_name = other.item_name; + locus = other.locus; + where_clause = other.where_clause; + function_params = other.function_params; + has_variadics = other.has_variadics; + variadic_outer_attrs = other.variadic_outer_attrs; + node_id = other.node_id; + + // guard to prevent null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + ExternalFunctionItem (ExternalFunctionItem &&other) = default; + ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Based on idea that nane should never be empty. + void mark_for_strip () override { item_name = ""; }; + bool is_marked_for_strip () const override { return item_name.empty (); }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + std::vector &get_function_params () + { + return function_params; + } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + + Identifier get_identifier () const { return item_name; }; + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternalFunctionItem *clone_external_item_impl () const override + { + return new ExternalFunctionItem (*this); + } +}; + +// An extern block AST node +class ExternBlock : public VisItem +{ + // bool has_abi; + std::string abi; + + // bool has_inner_attrs; + std::vector inner_attrs; + + // bool has_extern_items; + std::vector> extern_items; + + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + // Returns whether extern block has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + // Returns whether extern block has extern items. + bool has_extern_items () const { return !extern_items.empty (); } + + // Returns whether extern block has ABI name. + bool has_abi () const { return !abi.empty (); } + + std::string get_abi () const { return abi; } + + ExternBlock (std::string abi, + std::vector> extern_items, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), abi (std::move (abi)), + inner_attrs (std::move (inner_attrs)), + extern_items (std::move (extern_items)), locus (locus) + {} + + // Copy constructor with vector clone + ExternBlock (ExternBlock const &other) + : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs), + locus (other.locus), marked_for_strip (other.marked_for_strip) + { + extern_items.reserve (other.extern_items.size ()); + for (const auto &e : other.extern_items) + extern_items.push_back (e->clone_external_item ()); + } + + // Overloaded assignment operator with vector clone + ExternBlock &operator= (ExternBlock const &other) + { + VisItem::operator= (other); + abi = other.abi; + inner_attrs = other.inner_attrs; + locus = other.locus; + marked_for_strip = other.marked_for_strip; + + extern_items.reserve (other.extern_items.size ()); + for (const auto &e : other.extern_items) + extern_items.push_back (e->clone_external_item ()); + + return *this; + } + + // move constructors + ExternBlock (ExternBlock &&other) = default; + ExternBlock &operator= (ExternBlock &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: think of better way to do this + const std::vector> &get_extern_items () const + { + return extern_items; + } + std::vector> &get_extern_items () + { + return extern_items; + } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternBlock *clone_item_impl () const override + { + return new ExternBlock (*this); + } +}; + +// Replaced with forward decls - defined in "rust-macro.h" +class MacroItem; +class MacroRulesDefinition; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-macro.h b/gcc/rust/ast/rust-macro.h new file mode 100644 index 00000000000..ce515db0aad --- /dev/null +++ b/gcc/rust/ast/rust-macro.h @@ -0,0 +1,958 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_MACRO_H +#define RUST_AST_MACRO_H + +#include "rust-ast.h" +#include "rust-location.h" +#include + +namespace Rust { +namespace AST { + +// Decls as definitions moved to rust-ast.h +class MacroItem; + +class MacroFragSpec +{ +public: + enum Kind + { + BLOCK, + EXPR, + IDENT, + ITEM, + LIFETIME, + LITERAL, + META, + PAT, + PATH, + STMT, + TT, + TY, + VIS, + INVALID // not really a specifier, but used to mark invalid one passed in + }; + + MacroFragSpec (Kind kind) : kind (kind) {} + + static MacroFragSpec get_frag_spec_from_str (const std::string &str) + { + if (str == "block") + return MacroFragSpec (BLOCK); + else if (str == "expr") + return MacroFragSpec (EXPR); + else if (str == "ident") + return MacroFragSpec (IDENT); + else if (str == "item") + return MacroFragSpec (ITEM); + else if (str == "lifetime") + return MacroFragSpec (LIFETIME); + else if (str == "literal") + return MacroFragSpec (LITERAL); + else if (str == "meta") + return MacroFragSpec (META); + else if (str == "pat" || str == "pat_param") + return MacroFragSpec (PAT); + else if (str == "path") + return MacroFragSpec (PATH); + else if (str == "stmt") + return MacroFragSpec (STMT); + else if (str == "tt") + return MacroFragSpec (TT); + else if (str == "ty") + return MacroFragSpec (TY); + else if (str == "vis") + return MacroFragSpec (VIS); + else + { + // error_at("invalid string '%s' used as fragment specifier", + // str->c_str())); + return MacroFragSpec (INVALID); + } + } + + Kind get_kind () const { return kind; } + bool is_error () const { return kind == Kind::INVALID; } + + // Converts a frag spec enum item to a string form. + std::string as_string () const + { + switch (kind) + { + case BLOCK: + return "block"; + case EXPR: + return "expr"; + case IDENT: + return "ident"; + case ITEM: + return "item"; + case LIFETIME: + return "lifetime"; + case LITERAL: + return "literal"; + case META: + return "meta"; + case PAT: + return "pat"; + case PATH: + return "path"; + case STMT: + return "stmt"; + case TT: + return "tt"; + case TY: + return "ty"; + case VIS: + return "vis"; + case INVALID: + return "INVALID_FRAG_SPEC"; + default: + return "ERROR_MARK_STRING - unknown frag spec"; + } + } + + bool has_follow_set_restrictions () const + { + switch (kind) + { + case EXPR: + case STMT: + case PAT: + case PATH: + case TY: + case VIS: + return true; + default: + return false; + } + } + + bool has_follow_set_fragment_restrictions () const + { + switch (kind) + { + case PAT: + case TY: + case VIS: + return true; + default: + return false; + } + } + +private: + Kind kind; +}; + +// A macro match that has an identifier and fragment spec +class MacroMatchFragment : public MacroMatch +{ + Identifier ident; + MacroFragSpec frag_spec; + Location locus; + +public: + MacroMatchFragment (Identifier ident, MacroFragSpec frag_spec, Location locus) + : ident (std::move (ident)), frag_spec (frag_spec), locus (locus) + {} + + // Returns whether macro match fragment is in an error state. + bool is_error () const + { + return frag_spec.get_kind () == MacroFragSpec::INVALID; + } + + // Creates an error state macro match fragment. + static MacroMatchFragment create_error (Location locus) + { + return MacroMatchFragment (std::string (""), + MacroFragSpec (MacroFragSpec::Kind::INVALID), + locus); + } + + std::string as_string () const override; + Location get_match_locus () const override { return locus; }; + + void accept_vis (ASTVisitor &vis) override; + + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Fragment; + } + + Identifier get_ident () const { return ident; } + const MacroFragSpec &get_frag_spec () const { return frag_spec; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroMatchFragment *clone_macro_match_impl () const override + { + return new MacroMatchFragment (*this); + } +}; + +// A repetition macro match +class MacroMatchRepetition : public MacroMatch +{ +public: + enum MacroRepOp + { + NONE, + ANY, + ONE_OR_MORE, + ZERO_OR_ONE, + }; + +private: + std::vector > matches; + MacroRepOp op; + + // bool has_sep; + typedef Token MacroRepSep; + // any token except delimiters and repetition operators + std::unique_ptr sep; + Location locus; + +public: + // Returns whether macro match repetition has separator token. + bool has_sep () const { return sep != nullptr; } + + MacroMatchRepetition (std::vector > matches, + MacroRepOp op, std::unique_ptr sep, + Location locus) + : matches (std::move (matches)), op (op), sep (std::move (sep)), + locus (locus) + {} + + // Copy constructor with clone + MacroMatchRepetition (MacroMatchRepetition const &other) + : op (other.op), locus (other.locus) + { + // guard to protect from null pointer dereference + if (other.sep != nullptr) + sep = other.sep->clone_token (); + + matches.reserve (other.matches.size ()); + for (const auto &e : other.matches) + matches.push_back (e->clone_macro_match ()); + } + + // Overloaded assignment operator to clone + MacroMatchRepetition &operator= (MacroMatchRepetition const &other) + { + op = other.op; + locus = other.locus; + + // guard to protect from null pointer dereference + if (other.sep != nullptr) + sep = other.sep->clone_token (); + else + sep = nullptr; + + matches.reserve (other.matches.size ()); + for (const auto &e : other.matches) + matches.push_back (e->clone_macro_match ()); + + return *this; + } + + // move constructors + MacroMatchRepetition (MacroMatchRepetition &&other) = default; + MacroMatchRepetition &operator= (MacroMatchRepetition &&other) = default; + + std::string as_string () const override; + Location get_match_locus () const override { return locus; }; + + void accept_vis (ASTVisitor &vis) override; + + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Repetition; + } + + MacroRepOp get_op () const { return op; } + const std::unique_ptr &get_sep () const { return sep; } + std::vector > &get_matches () { return matches; } + const std::vector > &get_matches () const + { + return matches; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroMatchRepetition *clone_macro_match_impl () const override + { + return new MacroMatchRepetition (*this); + } +}; + +// can't inline due to polymorphism +class MacroMatcher : public MacroMatch +{ + DelimType delim_type; + std::vector > matches; + Location locus; + + // TODO: think of way to mark invalid that doesn't take up more space + bool is_invalid; + +public: + MacroMatcher (DelimType delim_type, + std::vector > matches, + Location locus) + : delim_type (delim_type), matches (std::move (matches)), locus (locus), + is_invalid (false) + {} + + // copy constructor with vector clone + MacroMatcher (MacroMatcher const &other) + : delim_type (other.delim_type), locus (other.locus) + { + matches.reserve (other.matches.size ()); + for (const auto &e : other.matches) + matches.push_back (e->clone_macro_match ()); + } + + // overloaded assignment operator with vector clone + MacroMatcher &operator= (MacroMatcher const &other) + { + delim_type = other.delim_type; + locus = other.locus; + + matches.reserve (other.matches.size ()); + for (const auto &e : other.matches) + matches.push_back (e->clone_macro_match ()); + + return *this; + } + + // move constructors + MacroMatcher (MacroMatcher &&other) = default; + MacroMatcher &operator= (MacroMatcher &&other) = default; + + // Creates an error state macro matcher. + static MacroMatcher create_error (Location locus) + { + return MacroMatcher (true, locus); + } + + // Returns whether MacroMatcher is in an error state. + bool is_error () const { return is_invalid; } + Location get_match_locus () const override { return locus; } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Matcher; + } + + DelimType get_delim_type () const { return delim_type; } + std::vector > &get_matches () { return matches; } + const std::vector > &get_matches () const + { + return matches; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroMatcher *clone_macro_match_impl () const override + { + return new MacroMatcher (*this); + } + + // constructor only used to create error matcher + MacroMatcher (bool is_invalid, Location locus) + : delim_type (PARENS), locus (locus), is_invalid (is_invalid) + {} +}; + +// TODO: inline? +struct MacroTranscriber +{ +private: + DelimTokenTree token_tree; + Location locus; + +public: + MacroTranscriber (DelimTokenTree token_tree, Location locus) + : token_tree (std::move (token_tree)), locus (locus) + {} + + std::string as_string () const { return token_tree.as_string (); } + + Location get_locus () const { return locus; } + + DelimTokenTree &get_token_tree () { return token_tree; } +}; + +// A macro rule? Matcher and transcriber pair? +struct MacroRule +{ +private: + MacroMatcher matcher; + MacroTranscriber transcriber; + Location locus; + +public: + MacroRule (MacroMatcher matcher, MacroTranscriber transcriber, Location locus) + : matcher (std::move (matcher)), transcriber (std::move (transcriber)), + locus (locus) + {} + + // Returns whether macro rule is in error state. + bool is_error () const { return matcher.is_error (); } + + // Creates an error state macro rule. + static MacroRule create_error (Location locus) + { + return MacroRule (MacroMatcher::create_error (locus), + MacroTranscriber (DelimTokenTree::create_empty (), + Location ()), + locus); + } + + Location get_locus () const { return locus; } + + std::string as_string () const; + + MacroMatcher &get_matcher () { return matcher; } + MacroTranscriber &get_transcriber () { return transcriber; } +}; + +// A macro rules definition item AST node +class MacroRulesDefinition : public MacroItem +{ + std::vector outer_attrs; + Identifier rule_name; + // MacroRulesDef rules_def; + // only curly without required semicolon at end + DelimType delim_type; + // MacroRules rules; + std::vector rules; // inlined form + Location locus; + + std::function + associated_transcriber; + // Since we can't compare std::functions, we need to use an extra boolean + bool is_builtin_rule; + + /** + * Default function to use as an associated transcriber. This function should + * never be called, hence the gcc_unreachable(). + * If this function is used, then the macro is not builtin and the compiler + * should make use of the actual rules. If the macro is builtin, then another + * associated transcriber should be used + */ + static ASTFragment dummy_builtin (Location, MacroInvocData &) + { + gcc_unreachable (); + return ASTFragment::create_error (); + } + + /* NOTE: in rustc, macro definitions are considered (and parsed as) a type + * of macro, whereas here they are considered part of the language itself. + * I am not aware of the implications of this decision. The rustc spec does + * mention that using the same parser for macro definitions and invocations + * is "extremely self-referential and non-intuitive". */ + +public: + std::string as_string () const override; + + MacroRulesDefinition (Identifier rule_name, DelimType delim_type, + std::vector rules, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)), + delim_type (delim_type), rules (std::move (rules)), locus (locus), + associated_transcriber (dummy_builtin), is_builtin_rule (false) + {} + + MacroRulesDefinition (Identifier builtin_name, DelimType delim_type, + std::function + associated_transcriber) + : outer_attrs (std::vector ()), rule_name (builtin_name), + delim_type (delim_type), rules (std::vector ()), + locus (Location ()), associated_transcriber (associated_transcriber), + is_builtin_rule (true) + {} + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if rule name is empty, so base stripping on that. + void mark_for_strip () override { rule_name = ""; } + bool is_marked_for_strip () const override { return rule_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + std::vector &get_macro_rules () { return rules; } + const std::vector &get_macro_rules () const { return rules; } + + Location get_locus () const override final { return locus; } + + Identifier get_rule_name () const { return rule_name; } + + std::vector &get_rules () { return rules; } + const std::vector &get_rules () const { return rules; } + + bool is_builtin () const { return is_builtin_rule; } + const std::function & + get_builtin_transcriber () const + { + rust_assert (is_builtin ()); + return associated_transcriber; + } + void set_builtin_transcriber ( + std::function transcriber) + { + associated_transcriber = transcriber; + is_builtin_rule = true; + } + + Kind get_ast_kind () const override { return Kind::MACRO_RULES_DEFINITION; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroRulesDefinition *clone_item_impl () const override + { + return new MacroRulesDefinition (*this); + } +}; + +/* AST node of a macro invocation, which is replaced by the macro result at + * compile time */ +class MacroInvocation : public TypeNoBounds, + public Pattern, + public MacroItem, + public TraitItem, + public TraitImplItem, + public InherentImplItem, + public ExternalItem, + public ExprWithoutBlock +{ + std::vector outer_attrs; + MacroInvocData invoc_data; + Location locus; + + // Important for when we actually expand the macro + bool is_semi_coloned; + + NodeId node_id; + +public: + std::string as_string () const override; + + MacroInvocation (MacroInvocData invoc_data, + std::vector outer_attrs, Location locus, + bool is_semi_coloned = false) + : outer_attrs (std::move (outer_attrs)), + invoc_data (std::move (invoc_data)), locus (locus), + is_semi_coloned (is_semi_coloned), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if path is empty, so base stripping on that. + void mark_for_strip () override { invoc_data.mark_for_strip (); } + bool is_marked_for_strip () const override + { + return invoc_data.is_marked_for_strip (); + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + NodeId get_pattern_node_id () const override final + { + return ExprWithoutBlock::get_node_id (); + } + + Kind get_ast_kind () const override { return Kind::MACRO_INVOCATION; } + + NodeId get_macro_node_id () const { return node_id; } + + MacroInvocData &get_invoc_data () { return invoc_data; } + + bool has_semicolon () const { return is_semi_coloned; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroInvocation *clone_pattern_impl () const final override + { + return clone_macro_invocation_impl (); + } + + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroInvocation *clone_expr_without_block_impl () const final override + { + return clone_macro_invocation_impl (); + } + + /* Use covariance to implement clone function as returning this object rather + * than base */ + MacroInvocation *clone_type_no_bounds_impl () const final override + { + return clone_macro_invocation_impl (); + } + + MacroInvocation *clone_external_item_impl () const final override + { + return clone_macro_invocation_impl (); + } + + /*virtual*/ MacroInvocation *clone_macro_invocation_impl () const + { + return new MacroInvocation (*this); + } + + Item *clone_item_impl () const override + { + return clone_macro_invocation_impl (); + } + + bool is_item () const override { return !has_semicolon (); } + + TraitItem *clone_trait_item_impl () const override + { + return clone_macro_invocation_impl (); + }; + + TraitImplItem *clone_trait_impl_item_impl () const override + { + return clone_macro_invocation_impl (); + }; + + InherentImplItem *clone_inherent_impl_item_impl () const override + { + return clone_macro_invocation_impl (); + } + + ExprWithoutBlock *to_stmt () const override + + { + auto new_impl = clone_macro_invocation_impl (); + new_impl->is_semi_coloned = true; + + return new_impl; + } +}; + +// more generic meta item path-only form +class MetaItemPath : public MetaItem +{ + SimplePath path; + +public: + MetaItemPath (SimplePath path) : path (std::move (path)) {} + + std::string as_string () const override { return path.as_string (); } + + void accept_vis (ASTVisitor &vis) override; + + // HACK: used to simplify parsing - returns non-empty only in this case + SimplePath to_path_item () const override + { + // this should copy construct - TODO ensure it does + return path; + } + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaItemPath *clone_meta_item_inner_impl () const override + { + return new MetaItemPath (*this); + } +}; + +// more generic meta item sequence form +class MetaItemSeq : public MetaItem +{ + SimplePath path; + std::vector > seq; + +public: + MetaItemSeq (SimplePath path, + std::vector > seq) + : path (std::move (path)), seq (std::move (seq)) + {} + + // copy constructor with vector clone + MetaItemSeq (const MetaItemSeq &other) : path (other.path) + { + seq.reserve (other.seq.size ()); + for (const auto &e : other.seq) + seq.push_back (e->clone_meta_item_inner ()); + } + + // overloaded assignment operator with vector clone + MetaItemSeq &operator= (const MetaItemSeq &other) + { + MetaItem::operator= (other); + path = other.path; + + seq.reserve (other.seq.size ()); + for (const auto &e : other.seq) + seq.push_back (e->clone_meta_item_inner ()); + + return *this; + } + + // default move constructors + MetaItemSeq (MetaItemSeq &&other) = default; + MetaItemSeq &operator= (MetaItemSeq &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaItemSeq *clone_meta_item_inner_impl () const override + { + return new MetaItemSeq (*this); + } +}; + +// Preferred specialisation for single-identifier meta items. +class MetaWord : public MetaItem +{ + Identifier ident; + Location ident_locus; + +public: + MetaWord (Identifier ident, Location ident_locus) + : ident (std::move (ident)), ident_locus (ident_locus) + {} + + std::string as_string () const override { return ident; } + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaWord *clone_meta_item_inner_impl () const override + { + return new MetaWord (*this); + } +}; + +// Preferred specialisation for "identifier '=' string literal" meta items. +class MetaNameValueStr : public MetaItem +{ + Identifier ident; + Location ident_locus; + + // NOTE: str stored without quotes + std::string str; + Location str_locus; + +public: + MetaNameValueStr (Identifier ident, Location ident_locus, std::string str, + Location str_locus) + : ident (std::move (ident)), ident_locus (ident_locus), + str (std::move (str)), str_locus (str_locus) + {} + + std::string as_string () const override + { + return ident + " = \"" + str + "\""; + } + + void accept_vis (ASTVisitor &vis) override; + + // HACK: used to simplify parsing - creates a copy of this + std::unique_ptr to_meta_name_value_str () const override + { + return std::unique_ptr (clone_meta_item_inner_impl ()); + } + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + + inline std::pair get_name_value_pair () const + { + return std::pair (ident, str); + } + + bool is_key_value_pair () const override { return true; } + +protected: + // Use covariance to implement clone function as returning this type + MetaNameValueStr *clone_meta_item_inner_impl () const override + { + return new MetaNameValueStr (*this); + } +}; + +// doubles up as MetaListIdents - determine via iterating through each path? +// Preferred specialisation for "identifier '(' SimplePath, SimplePath, ... ')'" +class MetaListPaths : public MetaItem +{ + Identifier ident; + Location ident_locus; + std::vector paths; + +public: + MetaListPaths (Identifier ident, Location ident_locus, + std::vector paths) + : ident (std::move (ident)), ident_locus (ident_locus), + paths (std::move (paths)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + +private: + bool check_path_exists_in_cfg (const Session &session, + const SimplePath &path) const; + +protected: + // Use covariance to implement clone function as returning this type + MetaListPaths *clone_meta_item_inner_impl () const override + { + return new MetaListPaths (*this); + } +}; + +// Preferred specialisation for "identifier '(' MetaNameValueStr, ... ')'" +class MetaListNameValueStr : public MetaItem +{ + Identifier ident; + Location ident_locus; + std::vector strs; + +public: + MetaListNameValueStr (Identifier ident, Location ident_locus, + std::vector strs) + : ident (std::move (ident)), ident_locus (ident_locus), + strs (std::move (strs)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + bool check_cfg_predicate (const Session &session) const override; + + Attribute to_attribute () const override; + +protected: + // Use covariance to implement clone function as returning this type + MetaListNameValueStr *clone_meta_item_inner_impl () const override + { + return new MetaListNameValueStr (*this); + } +}; + +// Object that parses macros from a token stream. +/* TODO: would "AttributeParser" be a better name? MetaItems are only for + * attributes, I believe */ +struct AttributeParser +{ +private: + // TODO: might as well rewrite to use lexer tokens + std::vector > token_stream; + int stream_pos; + +public: + AttributeParser (std::vector > token_stream, + int stream_start_pos = 0) + : token_stream (std::move (token_stream)), stream_pos (stream_start_pos) + {} + + ~AttributeParser () = default; + + std::vector > parse_meta_item_seq (); + +private: + // Parses a MetaItemInner. + std::unique_ptr parse_meta_item_inner (); + // Returns whether token can end a meta item. + bool is_end_meta_item_tok (TokenId id) const; + // Parses a simple path. + SimplePath parse_simple_path (); + // Parses a segment of a simple path (but not scope resolution operator). + SimplePathSegment parse_simple_path_segment (); + // Parses a MetaItemLitExpr. + std::unique_ptr parse_meta_item_lit (); + // Parses a literal. + Literal parse_literal (); + // Parses a meta item that begins with a simple path. + std::unique_ptr parse_path_meta_item (); + + // TODO: should this be const? + std::unique_ptr &peek_token (int i = 0) + { + return token_stream[stream_pos + i]; + } + + void skip_token (int i = 0) { stream_pos += 1 + i; } +}; +} // namespace AST +} // namespace Rust + +/* */ +namespace std { +template <> struct hash +{ + size_t operator() (const Rust::AST::MacroFragSpec::Kind &t) const noexcept + { + return size_t (t); + } +}; +} // namespace std + +#endif diff --git a/gcc/rust/ast/rust-path.h b/gcc/rust/ast/rust-path.h new file mode 100644 index 00000000000..cc79e278f05 --- /dev/null +++ b/gcc/rust/ast/rust-path.h @@ -0,0 +1,1297 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_PATH_H +#define RUST_AST_PATH_H +/* "Path" (identifier within namespaces, essentially) handling. Required include + * for virtually all AST-related functionality. */ + +#include "rust-ast.h" +#include "system.h" + +namespace Rust { +namespace AST { + +// The "identifier" (not generic args) aspect of each path expression segment +class PathIdentSegment +{ + std::string segment_name; + Location locus; + + // only allow identifiers, "super", "self", "Self", "crate", or "$crate" +public: + PathIdentSegment (std::string segment_name, Location locus) + : segment_name (std::move (segment_name)), locus (locus) + {} + + // Creates an error PathIdentSegment. + static PathIdentSegment create_error () + { + return PathIdentSegment ("", Location ()); + } + + // Returns whether PathIdentSegment is in an error state. + bool is_error () const { return segment_name.empty (); } + + std::string as_string () const { return segment_name; } + + Location get_locus () const { return locus; } + + bool is_super_segment () const { return as_string ().compare ("super") == 0; } + bool is_crate_segment () const { return as_string ().compare ("crate") == 0; } + bool is_lower_self () const { return as_string ().compare ("self") == 0; } + bool is_big_self () const { return as_string ().compare ("Self") == 0; } +}; + +// A binding of an identifier to a type used in generic arguments in paths +struct GenericArgsBinding +{ +private: + Identifier identifier; + std::unique_ptr type; + Location locus; + +public: + // Returns whether binding is in an error state. + bool is_error () const + { + return type == nullptr; + // and also identifier is empty, but cheaper computation + } + + // Creates an error state generic args binding. + static GenericArgsBinding create_error () + { + return GenericArgsBinding ("", nullptr); + } + + // Pointer type for type in constructor to enable polymorphism + GenericArgsBinding (Identifier ident, std::unique_ptr type_ptr, + Location locus = Location ()) + : identifier (std::move (ident)), type (std::move (type_ptr)), locus (locus) + {} + + // Copy constructor has to deep copy the type as it is a unique pointer + GenericArgsBinding (GenericArgsBinding const &other) + : identifier (other.identifier), locus (other.locus) + { + // guard to protect from null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // default destructor + ~GenericArgsBinding () = default; + + // Overload assignment operator to deep copy the pointed-to type + GenericArgsBinding &operator= (GenericArgsBinding const &other) + { + identifier = other.identifier; + locus = other.locus; + + // guard to protect from null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + GenericArgsBinding (GenericArgsBinding &&other) = default; + GenericArgsBinding &operator= (GenericArgsBinding &&other) = default; + + std::string as_string () const; + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + Location get_locus () const { return locus; } + + Identifier get_identifier () const { return identifier; } +}; + +/* Class representing a const generic application */ +class GenericArg +{ +public: + /** + * const generic arguments cannot always be differentiated with generic type + * arguments during parsing, e.g: + * ```rust + * let a: Foo; + * ``` + * + * Is N a type? A constant defined elsewhere? The parser cannot know, and must + * not draw any conclusions. We must wait until later passes of the compiler + * to decide whether this refers to a constant item or a type. + * + * On the other hand, simple expressions like literals or block expressions + * will always be constant expressions: There is no ambiguity at all. + */ + enum class Kind + { + Error, + Const, // A const value + Type, // A type argument (not discernable during parsing) + Either, // Either a type or a const value, cleared up during resolving + }; + + static GenericArg create_error () + { + return GenericArg (nullptr, nullptr, "", Kind::Error, Location ()); + } + + static GenericArg create_const (std::unique_ptr expression) + { + auto locus = expression->get_locus (); + return GenericArg (std::move (expression), nullptr, "", Kind::Const, locus); + } + + static GenericArg create_type (std::unique_ptr type) + { + auto locus = type->get_locus (); + return GenericArg (nullptr, std::move (type), "", Kind::Type, locus); + } + + static GenericArg create_ambiguous (Identifier path, Location locus) + { + return GenericArg (nullptr, nullptr, std::move (path), Kind::Either, locus); + } + + GenericArg (const GenericArg &other) + : path (other.path), kind (other.kind), locus (other.locus) + { + if (other.expression) + expression = other.expression->clone_expr (); + if (other.type) + type = other.type->clone_type (); + } + + GenericArg operator= (const GenericArg &other) + { + kind = other.kind; + path = other.path; + locus = other.locus; + + if (other.expression) + expression = other.expression->clone_expr (); + if (other.type) + type = other.type->clone_type (); + + return *this; + } + + bool is_error () const { return kind == Kind::Error; } + + Kind get_kind () const { return kind; } + const Location &get_locus () const { return locus; } + + std::unique_ptr &get_expression () + { + rust_assert (kind == Kind::Const); + + return expression; + } + + std::unique_ptr &get_type () + { + rust_assert (kind == Kind::Type); + + return type; + } + + const std::string &get_path () const + { + rust_assert (kind == Kind::Either); + + return path; + } + + std::string as_string () const + { + switch (get_kind ()) + { + case Kind::Error: + gcc_unreachable (); + case Kind::Either: + return "Ambiguous: " + path; + case Kind::Const: + return "Const: { " + expression->as_string () + " }"; + case Kind::Type: + return "Type: " + type->as_string (); + } + + return ""; + } + + /** + * Disambiguate an ambiguous generic argument to a const generic argument, + * unequivocally + */ + GenericArg disambiguate_to_const () const; + + /** + * Disambiguate an ambiguous generic argument to a type argument, + * unequivocally + */ + GenericArg disambiguate_to_type () const; + +private: + GenericArg (std::unique_ptr expression, std::unique_ptr type, + Identifier path, Kind kind, Location locus) + : expression (std::move (expression)), type (std::move (type)), + path (std::move (path)), kind (kind), locus (locus) + {} + + /** + * Expression associated with a `Clear` const generic application + * A null pointer here is allowed in the case that the const argument is + * ambiguous. + */ + std::unique_ptr expression; + + /** + * If the argument ends up being a type argument instead. A null pointer will + * be present here until the resolving phase. + */ + std::unique_ptr type; + + /** + * Optional path which cannot be differentiated between a constant item and + * a type. Only used for ambiguous const generic arguments, otherwise + * empty. + */ + Identifier path; + + /* Which kind of const generic application are we dealing with */ + Kind kind; + + Location locus; +}; + +/** + * Representation of const generic parameters + */ +class ConstGenericParam : public GenericParam +{ + /* Name of the parameter */ + Identifier name; + + /* Mandatory type of the const parameter - a null pointer is an error */ + std::unique_ptr type; + + /** + * Default value for the const generic parameter + */ + GenericArg default_value; + + Attribute outer_attr; + Location locus; + +public: + ConstGenericParam (Identifier name, std::unique_ptr type, + GenericArg default_value, Attribute outer_attr, + Location locus) + : name (name), type (std::move (type)), + default_value (std::move (default_value)), outer_attr (outer_attr), + locus (locus) + {} + + ConstGenericParam (const ConstGenericParam &other) + : GenericParam (), name (other.name), type (other.type->clone_type ()), + default_value (other.default_value), outer_attr (other.outer_attr), + locus (other.locus) + {} + + bool has_type () const { return type != nullptr; } + bool has_default_value () const { return !default_value.is_error (); } + + const Identifier &get_name () const { return name; } + + std::unique_ptr &get_type () + { + rust_assert (has_type ()); + + return type; + } + + GenericArg &get_default_value () + { + rust_assert (has_default_value ()); + + return default_value; + } + + const GenericArg &get_default_value () const + { + rust_assert (has_default_value ()); + + return default_value; + } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Location get_locus () const override final { return locus; } + + Kind get_kind () const override final { return Kind::Const; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ConstGenericParam *clone_generic_param_impl () const override + { + return new ConstGenericParam (*this); + } +}; + +// Generic arguments allowed in each path expression segment - inline? +struct GenericArgs +{ + std::vector lifetime_args; + std::vector generic_args; + std::vector binding_args; + Location locus; + +public: + // Returns true if there are any generic arguments + bool has_generic_args () const + { + return !(lifetime_args.empty () && generic_args.empty () + && binding_args.empty ()); + } + + GenericArgs (std::vector lifetime_args, + std::vector generic_args, + std::vector binding_args, + Location locus = Location ()) + : lifetime_args (std::move (lifetime_args)), + generic_args (std::move (generic_args)), + binding_args (std::move (binding_args)), locus (locus) + {} + + // copy constructor with vector clone + GenericArgs (GenericArgs const &other) + : lifetime_args (other.lifetime_args), generic_args (other.generic_args), + binding_args (other.binding_args), locus (other.locus) + {} + + ~GenericArgs () = default; + + // overloaded assignment operator to vector clone + GenericArgs &operator= (GenericArgs const &other) + { + lifetime_args = other.lifetime_args; + generic_args = other.generic_args; + binding_args = other.binding_args; + locus = other.locus; + + return *this; + } + + // move constructors + GenericArgs (GenericArgs &&other) = default; + GenericArgs &operator= (GenericArgs &&other) = default; + + // Creates an empty GenericArgs (no arguments) + static GenericArgs create_empty () { return GenericArgs ({}, {}, {}); } + + std::string as_string () const; + + // TODO: is this better? Or is a "vis_pattern" better? + std::vector &get_generic_args () { return generic_args; } + + // TODO: is this better? Or is a "vis_pattern" better? + std::vector &get_binding_args () { return binding_args; } + + std::vector &get_lifetime_args () { return lifetime_args; }; + + Location get_locus () { return locus; } +}; + +/* A segment of a path in expression, including an identifier aspect and maybe + * generic args */ +class PathExprSegment +{ // or should this extend PathIdentSegment? +private: + PathIdentSegment segment_name; + GenericArgs generic_args; + Location locus; + NodeId node_id; + +public: + // Returns true if there are any generic arguments + bool has_generic_args () const { return generic_args.has_generic_args (); } + + // Constructor for segment (from IdentSegment and GenericArgs) + PathExprSegment (PathIdentSegment segment_name, Location locus, + GenericArgs generic_args = GenericArgs::create_empty ()) + : segment_name (std::move (segment_name)), + generic_args (std::move (generic_args)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + /* Constructor for segment with generic arguments (from segment name and all + * args) */ + PathExprSegment (std::string segment_name, Location locus, + std::vector lifetime_args = {}, + std::vector generic_args = {}, + std::vector binding_args = {}) + : segment_name (PathIdentSegment (std::move (segment_name), locus)), + generic_args (GenericArgs (std::move (lifetime_args), + std::move (generic_args), + std::move (binding_args))), + locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Returns whether path expression segment is in an error state. + bool is_error () const { return segment_name.is_error (); } + + // Creates an error-state path expression segment. + static PathExprSegment create_error () + { + return PathExprSegment (PathIdentSegment::create_error (), Location ()); + } + + std::string as_string () const; + + Location get_locus () const { return locus; } + + // TODO: is this better? Or is a "vis_pattern" better? + GenericArgs &get_generic_args () + { + rust_assert (has_generic_args ()); + return generic_args; + } + + PathIdentSegment &get_ident_segment () { return segment_name; } + const PathIdentSegment &get_ident_segment () const { return segment_name; } + + NodeId get_node_id () const { return node_id; } + + bool is_super_path_seg () const + { + return !has_generic_args () && get_ident_segment ().is_super_segment (); + } + + bool is_crate_path_seg () const + { + return !has_generic_args () && get_ident_segment ().is_crate_segment (); + } + bool is_lower_self_seg () const + { + return !has_generic_args () && get_ident_segment ().is_lower_self (); + } +}; + +// AST node representing a pattern that involves a "path" - abstract base +// class +class PathPattern : public Pattern +{ + std::vector segments; + +protected: + PathPattern (std::vector segments) + : segments (std::move (segments)) + {} + + // Returns whether path has segments. + bool has_segments () const { return !segments.empty (); } + + /* Converts path segments to their equivalent SimplePath segments if + * possible, and creates a SimplePath from them. */ + SimplePath convert_to_simple_path (bool with_opening_scope_resolution) const; + + // Removes all segments of the path. + void remove_all_segments () + { + segments.clear (); + segments.shrink_to_fit (); + } + +public: + /* Returns whether the path is a single segment (excluding qualified path + * initial as segment). */ + bool is_single_segment () const { return segments.size () == 1; } + + std::string as_string () const override; + + // TODO: this seems kinda dodgy + std::vector &get_segments () { return segments; } + const std::vector &get_segments () const { return segments; } +}; + +/* AST node representing a path-in-expression pattern (path that allows + * generic arguments) */ +class PathInExpression : public PathPattern, public PathExpr +{ + std::vector outer_attrs; + bool has_opening_scope_resolution; + Location locus; + NodeId _node_id; + +public: + std::string as_string () const override; + + // Constructor + PathInExpression (std::vector path_segments, + std::vector outer_attrs, Location locus, + bool has_opening_scope_resolution = false) + : PathPattern (std::move (path_segments)), + outer_attrs (std::move (outer_attrs)), + has_opening_scope_resolution (has_opening_scope_resolution), + locus (locus), _node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Creates an error state path in expression. + static PathInExpression create_error () + { + return PathInExpression ({}, {}, Location ()); + } + + // Returns whether path in expression is in an error state. + bool is_error () const { return !has_segments (); } + + /* Converts PathInExpression to SimplePath if possible (i.e. no generic + * arguments). Otherwise returns an empty SimplePath. */ + SimplePath as_simple_path () const + { + /* delegate to parent class as can't access segments. however, + * QualifiedPathInExpression conversion to simple path wouldn't make + * sense, so the method in the parent class should be protected, not + * public. Have to pass in opening scope resolution as parent class has no + * access to it. + */ + return convert_to_simple_path (has_opening_scope_resolution); + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if path is empty (error state), so base stripping on that. + void mark_for_strip () override { remove_all_segments (); } + bool is_marked_for_strip () const override { return is_error (); } + + bool opening_scope_resolution () const + { + return has_opening_scope_resolution; + } + + NodeId get_node_id () const override { return _node_id; } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + NodeId get_pattern_node_id () const override final { return get_node_id (); } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + PathInExpression *clone_pattern_impl () const final override + { + return clone_path_in_expression_impl (); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + PathInExpression *clone_expr_without_block_impl () const final override + { + return clone_path_in_expression_impl (); + } + + /*virtual*/ PathInExpression *clone_path_in_expression_impl () const + { + return new PathInExpression (*this); + } +}; + +/* Base class for segments used in type paths - not abstract (represents an + * ident-only segment) */ +class TypePathSegment +{ +public: + enum SegmentType + { + REG, + GENERIC, + FUNCTION + }; + +private: + PathIdentSegment ident_segment; + Location locus; + +protected: + /* This is protected because it is only really used by derived classes, not + * the base. */ + bool has_separating_scope_resolution; + NodeId node_id; + + // Clone function implementation - not pure virtual as overrided by + // subclasses + virtual TypePathSegment *clone_type_path_segment_impl () const + { + return new TypePathSegment (*this); + } + +public: + virtual ~TypePathSegment () {} + + virtual SegmentType get_type () const { return SegmentType::REG; } + + // Unique pointer custom clone function + std::unique_ptr clone_type_path_segment () const + { + return std::unique_ptr (clone_type_path_segment_impl ()); + } + + TypePathSegment (PathIdentSegment ident_segment, + bool has_separating_scope_resolution, Location locus) + : ident_segment (std::move (ident_segment)), locus (locus), + has_separating_scope_resolution (has_separating_scope_resolution), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + TypePathSegment (std::string segment_name, + bool has_separating_scope_resolution, Location locus) + : ident_segment (PathIdentSegment (std::move (segment_name), locus)), + locus (locus), + has_separating_scope_resolution (has_separating_scope_resolution), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + virtual std::string as_string () const { return ident_segment.as_string (); } + + /* Returns whether the type path segment is in an error state. May be + * virtual in future. */ + bool is_error () const { return ident_segment.is_error (); } + + /* Returns whether segment is identifier only (as opposed to generic args or + * function). Overridden in derived classes with other segments. */ + virtual bool is_ident_only () const { return true; } + + Location get_locus () const { return locus; } + + // not pure virtual as class not abstract + virtual void accept_vis (ASTVisitor &vis); + + bool get_separating_scope_resolution () const + { + return has_separating_scope_resolution; + } + + PathIdentSegment &get_ident_segment () { return ident_segment; }; + const PathIdentSegment &get_ident_segment () const { return ident_segment; }; + + NodeId get_node_id () const { return node_id; } + + bool is_crate_path_seg () const + { + return get_ident_segment ().is_crate_segment (); + } + bool is_super_path_seg () const + { + return get_ident_segment ().is_super_segment (); + } + bool is_big_self_seg () const { return get_ident_segment ().is_big_self (); } + bool is_lower_self_seg () const + { + return get_ident_segment ().is_lower_self (); + } +}; + +// Segment used in type path with generic args +class TypePathSegmentGeneric : public TypePathSegment +{ + GenericArgs generic_args; + +public: + SegmentType get_type () const override { return SegmentType::GENERIC; } + + bool has_generic_args () const { return generic_args.has_generic_args (); } + + bool is_ident_only () const override { return false; } + + // Constructor with PathIdentSegment and GenericArgs + TypePathSegmentGeneric (PathIdentSegment ident_segment, + bool has_separating_scope_resolution, + GenericArgs generic_args, Location locus) + : TypePathSegment (std::move (ident_segment), + has_separating_scope_resolution, locus), + generic_args (std::move (generic_args)) + {} + + // Constructor from segment name and all args + TypePathSegmentGeneric (std::string segment_name, + bool has_separating_scope_resolution, + std::vector lifetime_args, + std::vector generic_args, + std::vector binding_args, + Location locus) + : TypePathSegment (std::move (segment_name), + has_separating_scope_resolution, locus), + generic_args (GenericArgs (std::move (lifetime_args), + std::move (generic_args), + std::move (binding_args))) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_pattern" better? + GenericArgs &get_generic_args () + { + rust_assert (has_generic_args ()); + return generic_args; + } + +protected: + // Use covariance to override base class method + TypePathSegmentGeneric *clone_type_path_segment_impl () const override + { + return new TypePathSegmentGeneric (*this); + } +}; + +// A function as represented in a type path +struct TypePathFunction +{ +private: + // TODO: remove + /*bool has_inputs; + TypePathFnInputs inputs;*/ + // inlined from TypePathFnInputs + std::vector > inputs; + + // bool has_type; + std::unique_ptr return_type; + + // FIXME: think of better way to mark as invalid than taking up storage + bool is_invalid; + + Location locus; + +protected: + // Constructor only used to create invalid type path functions. + TypePathFunction (bool is_invalid, Location locus) + : is_invalid (is_invalid), locus (locus) + {} + +public: + // Returns whether the return type of the function has been specified. + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether the function has inputs. + bool has_inputs () const { return !inputs.empty (); } + + // Returns whether function is in an error state. + bool is_error () const { return is_invalid; } + + // Creates an error state function. + static TypePathFunction create_error () + { + return TypePathFunction (true, Location ()); + } + + // Constructor + TypePathFunction (std::vector > inputs, Location locus, + std::unique_ptr type = nullptr) + : inputs (std::move (inputs)), return_type (std::move (type)), + is_invalid (false), locus (locus) + {} + + // Copy constructor with clone + TypePathFunction (TypePathFunction const &other) + : is_invalid (other.is_invalid) + { + // guard to protect from null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + inputs.reserve (other.inputs.size ()); + for (const auto &e : other.inputs) + inputs.push_back (e->clone_type ()); + } + + ~TypePathFunction () = default; + + // Overloaded assignment operator to clone type + TypePathFunction &operator= (TypePathFunction const &other) + { + is_invalid = other.is_invalid; + + // guard to protect from null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + inputs.reserve (other.inputs.size ()); + for (const auto &e : other.inputs) + inputs.push_back (e->clone_type ()); + + return *this; + } + + // move constructors + TypePathFunction (TypePathFunction &&other) = default; + TypePathFunction &operator= (TypePathFunction &&other) = default; + + std::string as_string () const; + + // TODO: this mutable getter seems really dodgy. Think up better way. + const std::vector > &get_params () const + { + return inputs; + } + std::vector > &get_params () { return inputs; } + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } +}; + +// Segment used in type path with a function argument +class TypePathSegmentFunction : public TypePathSegment +{ + TypePathFunction function_path; + +public: + SegmentType get_type () const override { return SegmentType::FUNCTION; } + + // Constructor with PathIdentSegment and TypePathFn + TypePathSegmentFunction (PathIdentSegment ident_segment, + bool has_separating_scope_resolution, + TypePathFunction function_path, Location locus) + : TypePathSegment (std::move (ident_segment), + has_separating_scope_resolution, locus), + function_path (std::move (function_path)) + {} + + // Constructor with segment name and TypePathFn + TypePathSegmentFunction (std::string segment_name, + bool has_separating_scope_resolution, + TypePathFunction function_path, Location locus) + : TypePathSegment (std::move (segment_name), + has_separating_scope_resolution, locus), + function_path (std::move (function_path)) + {} + + std::string as_string () const override; + + bool is_ident_only () const override { return false; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_pattern" better? + TypePathFunction &get_type_path_function () + { + rust_assert (!function_path.is_error ()); + return function_path; + } + +protected: + // Use covariance to override base class method + TypePathSegmentFunction *clone_type_path_segment_impl () const override + { + return new TypePathSegmentFunction (*this); + } +}; + +// Path used inside types +class TypePath : public TypeNoBounds +{ + bool has_opening_scope_resolution; + std::vector > segments; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TypePath *clone_type_no_bounds_impl () const override + { + return new TypePath (*this); + } + +public: + /* Returns whether the TypePath has an opening scope resolution operator + * (i.e. is global path or crate-relative path, not module-relative) */ + bool has_opening_scope_resolution_op () const + { + return has_opening_scope_resolution; + } + + // Returns whether the TypePath is in an invalid state. + bool is_error () const { return segments.empty (); } + + // Creates an error state TypePath. + static TypePath create_error () + { + return TypePath (std::vector > (), + Location ()); + } + + // Constructor + TypePath (std::vector > segments, + Location locus, bool has_opening_scope_resolution = false) + : TypeNoBounds (), + has_opening_scope_resolution (has_opening_scope_resolution), + segments (std::move (segments)), locus (locus) + {} + + // Copy constructor with vector clone + TypePath (TypePath const &other) + : has_opening_scope_resolution (other.has_opening_scope_resolution), + locus (other.locus) + { + segments.reserve (other.segments.size ()); + for (const auto &e : other.segments) + segments.push_back (e->clone_type_path_segment ()); + } + + // Overloaded assignment operator with clone + TypePath &operator= (TypePath const &other) + { + has_opening_scope_resolution = other.has_opening_scope_resolution; + locus = other.locus; + + segments.reserve (other.segments.size ()); + for (const auto &e : other.segments) + segments.push_back (e->clone_type_path_segment ()); + + return *this; + } + + // move constructors + TypePath (TypePath &&other) = default; + TypePath &operator= (TypePath &&other) = default; + + std::string as_string () const override; + + /* Converts TypePath to SimplePath if possible (i.e. no generic or function + * arguments). Otherwise returns an empty SimplePath. */ + SimplePath as_simple_path () const; + + // Creates a trait bound with a clone of this type path as its only element. + TraitBound *to_trait_bound (bool in_parens) const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this seems kinda dodgy + std::vector > &get_segments () + { + return segments; + } + const std::vector > &get_segments () const + { + return segments; + } + + size_t get_num_segments () const { return segments.size (); } +}; + +struct QualifiedPathType +{ +private: + std::unique_ptr type_to_invoke_on; + TypePath trait_path; + Location locus; + NodeId node_id; + +public: + // Constructor + QualifiedPathType (std::unique_ptr invoke_on_type, + Location locus = Location (), + TypePath trait_path = TypePath::create_error ()) + : type_to_invoke_on (std::move (invoke_on_type)), + trait_path (std::move (trait_path)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor uses custom deep copy for Type to preserve polymorphism + QualifiedPathType (QualifiedPathType const &other) + : trait_path (other.trait_path), locus (other.locus) + { + node_id = other.node_id; + // guard to prevent null dereference + if (other.type_to_invoke_on != nullptr) + type_to_invoke_on = other.type_to_invoke_on->clone_type (); + } + + // default destructor + ~QualifiedPathType () = default; + + // overload assignment operator to use custom clone method + QualifiedPathType &operator= (QualifiedPathType const &other) + { + node_id = other.node_id; + trait_path = other.trait_path; + locus = other.locus; + + // guard to prevent null dereference + if (other.type_to_invoke_on != nullptr) + type_to_invoke_on = other.type_to_invoke_on->clone_type (); + else + type_to_invoke_on = nullptr; + + return *this; + } + + // move constructor + QualifiedPathType (QualifiedPathType &&other) = default; + QualifiedPathType &operator= (QualifiedPathType &&other) = default; + + // Returns whether the qualified path type has a rebind as clause. + bool has_as_clause () const { return !trait_path.is_error (); } + + // Returns whether the qualified path type is in an error state. + bool is_error () const { return type_to_invoke_on == nullptr; } + + // Creates an error state qualified path type. + static QualifiedPathType create_error () + { + return QualifiedPathType (nullptr); + } + + std::string as_string () const; + + Location get_locus () const { return locus; } + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_type () + { + rust_assert (type_to_invoke_on != nullptr); + return type_to_invoke_on; + } + + // TODO: is this better? Or is a "vis_pattern" better? + TypePath &get_as_type_path () + { + rust_assert (has_as_clause ()); + return trait_path; + } + + NodeId get_node_id () const { return node_id; } +}; + +/* AST node representing a qualified path-in-expression pattern (path that + * allows specifying trait functions) */ +class QualifiedPathInExpression : public PathPattern, public PathExpr +{ + std::vector outer_attrs; + QualifiedPathType path_type; + Location locus; + NodeId _node_id; + +public: + std::string as_string () const override; + + QualifiedPathInExpression (QualifiedPathType qual_path_type, + std::vector path_segments, + std::vector outer_attrs, Location locus) + : PathPattern (std::move (path_segments)), + outer_attrs (std::move (outer_attrs)), + path_type (std::move (qual_path_type)), locus (locus), + _node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + /* TODO: maybe make a shortcut constructor that has QualifiedPathType + * elements as params */ + + // Returns whether qualified path in expression is in an error state. + bool is_error () const { return path_type.is_error (); } + + // Creates an error qualified path in expression. + static QualifiedPathInExpression create_error () + { + return QualifiedPathInExpression (QualifiedPathType::create_error (), {}, + {}, Location ()); + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if path_type is error, so base stripping on that. + void mark_for_strip () override + { + path_type = QualifiedPathType::create_error (); + } + bool is_marked_for_strip () const override { return is_error (); } + + // TODO: is this better? Or is a "vis_pattern" better? + QualifiedPathType &get_qualified_path_type () + { + rust_assert (!path_type.is_error ()); + return path_type; + } + + const std::vector &get_outer_attrs () const { return outer_attrs; } + std::vector &get_outer_attrs () { return outer_attrs; } + + void set_outer_attrs (std::vector new_attrs) override + { + outer_attrs = std::move (new_attrs); + } + + NodeId get_node_id () const override { return _node_id; } + + NodeId get_pattern_node_id () const override final { return get_node_id (); } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + QualifiedPathInExpression *clone_pattern_impl () const final override + { + return clone_qual_path_in_expression_impl (); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + QualifiedPathInExpression * + clone_expr_without_block_impl () const final override + { + return clone_qual_path_in_expression_impl (); + } + + /*virtual*/ QualifiedPathInExpression * + clone_qual_path_in_expression_impl () const + { + return new QualifiedPathInExpression (*this); + } +}; + +/* Represents a qualified path in a type; used for disambiguating trait + * function calls */ +class QualifiedPathInType : public TypeNoBounds +{ + QualifiedPathType path_type; + std::unique_ptr associated_segment; + std::vector > segments; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + QualifiedPathInType *clone_type_no_bounds_impl () const override + { + return new QualifiedPathInType (*this); + } + +public: + QualifiedPathInType ( + QualifiedPathType qual_path_type, + std::unique_ptr associated_segment, + std::vector > path_segments, + Location locus) + : path_type (std::move (qual_path_type)), + associated_segment (std::move (associated_segment)), + segments (std::move (path_segments)), locus (locus) + {} + + /* TODO: maybe make a shortcut constructor that has QualifiedPathType + * elements as params */ + + // Copy constructor with vector clone + QualifiedPathInType (QualifiedPathInType const &other) + : path_type (other.path_type), locus (other.locus) + { + segments.reserve (other.segments.size ()); + for (const auto &e : other.segments) + segments.push_back (e->clone_type_path_segment ()); + } + + // Overloaded assignment operator with vector clone + QualifiedPathInType &operator= (QualifiedPathInType const &other) + { + path_type = other.path_type; + locus = other.locus; + + segments.reserve (other.segments.size ()); + for (const auto &e : other.segments) + segments.push_back (e->clone_type_path_segment ()); + + return *this; + } + + // move constructors + QualifiedPathInType (QualifiedPathInType &&other) = default; + QualifiedPathInType &operator= (QualifiedPathInType &&other) = default; + + // Returns whether qualified path in type is in an error state. + bool is_error () const { return path_type.is_error (); } + + // Creates an error state qualified path in type. + static QualifiedPathInType create_error () + { + return QualifiedPathInType ( + QualifiedPathType::create_error (), nullptr, + std::vector > (), Location ()); + } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_pattern" better? + QualifiedPathType &get_qualified_path_type () + { + rust_assert (!path_type.is_error ()); + return path_type; + } + + std::unique_ptr &get_associated_segment () + { + return associated_segment; + } + + // TODO: this seems kinda dodgy + std::vector > &get_segments () + { + return segments; + } + const std::vector > &get_segments () const + { + return segments; + } + + Location get_locus () const override final { return locus; } +}; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-pattern.h b/gcc/rust/ast/rust-pattern.h new file mode 100644 index 00000000000..247af5dbe05 --- /dev/null +++ b/gcc/rust/ast/rust-pattern.h @@ -0,0 +1,1576 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_PATTERN_H +#define RUST_AST_PATTERN_H + +#include "rust-ast.h" + +namespace Rust { +namespace AST { +// Literal pattern AST node (comparing to a literal) +class LiteralPattern : public Pattern +{ + Literal lit; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + // Constructor for a literal pattern + LiteralPattern (Literal lit, Location locus) + : lit (std::move (lit)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + LiteralPattern (std::string val, Literal::LitType type, Location locus) + : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)), + locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + + Literal &get_literal () { return lit; } + + const Literal &get_literal () const { return lit; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + virtual LiteralPattern *clone_pattern_impl () const override + { + return new LiteralPattern (*this); + } +}; + +// Identifier pattern AST node (bind value matched to a variable) +class IdentifierPattern : public Pattern +{ + Identifier variable_ident; + bool is_ref; + bool is_mut; + + // bool has_pattern; + std::unique_ptr to_bind; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + // Returns whether the IdentifierPattern has a pattern to bind. + bool has_pattern_to_bind () const { return to_bind != nullptr; } + + // Constructor + IdentifierPattern (Identifier ident, Location locus, bool is_ref = false, + bool is_mut = false, + std::unique_ptr to_bind = nullptr) + : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref), + is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + IdentifierPattern (NodeId node_id, Identifier ident, Location locus, + bool is_ref = false, bool is_mut = false, + std::unique_ptr to_bind = nullptr) + : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref), + is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus), + node_id (node_id) + {} + + // Copy constructor with clone + IdentifierPattern (IdentifierPattern const &other) + : variable_ident (other.variable_ident), is_ref (other.is_ref), + is_mut (other.is_mut), locus (other.locus), node_id (other.node_id) + { + // fix to get prevent null pointer dereference + if (other.to_bind != nullptr) + to_bind = other.to_bind->clone_pattern (); + } + + // Overload assignment operator to use clone + IdentifierPattern &operator= (IdentifierPattern const &other) + { + variable_ident = other.variable_ident; + is_ref = other.is_ref; + is_mut = other.is_mut; + locus = other.locus; + node_id = other.node_id; + + // fix to prevent null pointer dereference + if (other.to_bind != nullptr) + to_bind = other.to_bind->clone_pattern (); + else + to_bind = nullptr; + + return *this; + } + + // default move semantics + IdentifierPattern (IdentifierPattern &&other) = default; + IdentifierPattern &operator= (IdentifierPattern &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_pattern_to_bind () + { + rust_assert (has_pattern_to_bind ()); + return to_bind; + } + + Identifier get_ident () const { return variable_ident; } + + bool get_is_mut () const { return is_mut; } + bool get_is_ref () const { return is_ref; } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + IdentifierPattern *clone_pattern_impl () const override + { + return new IdentifierPattern (*this); + } +}; + +// AST node for using the '_' wildcard "match any value" pattern +class WildcardPattern : public Pattern +{ + Location locus; + NodeId node_id; + +public: + std::string as_string () const override { return std::string (1, '_'); } + + WildcardPattern (Location locus) + : locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + WildcardPattern *clone_pattern_impl () const override + { + return new WildcardPattern (*this); + } +}; + +// Base range pattern bound (lower or upper limit) - abstract +class RangePatternBound +{ +public: + enum RangePatternBoundType + { + LITERAL, + PATH, + QUALPATH + }; + + virtual ~RangePatternBound () {} + + // Unique pointer custom clone function + std::unique_ptr clone_range_pattern_bound () const + { + return std::unique_ptr ( + clone_range_pattern_bound_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual RangePatternBoundType get_bound_type () const = 0; + +protected: + // pure virtual as RangePatternBound is abstract + virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0; +}; + +// Literal-based pattern bound +class RangePatternBoundLiteral : public RangePatternBound +{ + Literal literal; + /* Can only be a char, byte, int, or float literal - same impl here as + * previously */ + + // Minus prefixed to literal (if integer or floating-point) + bool has_minus; + + Location locus; + +public: + // Constructor + RangePatternBoundLiteral (Literal literal, Location locus, + bool has_minus = false) + : literal (literal), has_minus (has_minus), locus (locus) + {} + + std::string as_string () const override; + + Literal get_literal () const { return literal; } + + bool get_has_minus () const { return has_minus; } + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + RangePatternBoundType get_bound_type () const override + { + return RangePatternBoundType::LITERAL; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override + { + return new RangePatternBoundLiteral (*this); + } +}; + +// Path-based pattern bound +class RangePatternBoundPath : public RangePatternBound +{ + PathInExpression path; + + /* TODO: should this be refactored so that PathInExpression is a subclass of + * RangePatternBound? */ + +public: + RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {} + + std::string as_string () const override { return path.as_string (); } + + Location get_locus () const { return path.get_locus (); } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems kinda dodgy + PathInExpression &get_path () { return path; } + const PathInExpression &get_path () const { return path; } + + RangePatternBoundType get_bound_type () const override + { + return RangePatternBoundType::PATH; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangePatternBoundPath *clone_range_pattern_bound_impl () const override + { + return new RangePatternBoundPath (*this); + } +}; + +// Qualified path-based pattern bound +class RangePatternBoundQualPath : public RangePatternBound +{ + QualifiedPathInExpression path; + + /* TODO: should this be refactored so that QualifiedPathInExpression is a + * subclass of RangePatternBound? */ + +public: + RangePatternBoundQualPath (QualifiedPathInExpression path) + : path (std::move (path)) + {} + + std::string as_string () const override { return path.as_string (); } + + Location get_locus () const { return path.get_locus (); } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems kinda dodgy + QualifiedPathInExpression &get_qualified_path () { return path; } + const QualifiedPathInExpression &get_qualified_path () const { return path; } + + RangePatternBoundType get_bound_type () const override + { + return RangePatternBoundType::QUALPATH; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override + { + return new RangePatternBoundQualPath (*this); + } +}; + +// AST node for matching within a certain range (range pattern) +class RangePattern : public Pattern +{ + std::unique_ptr lower; + std::unique_ptr upper; + + bool has_ellipsis_syntax; + + /* location only stored to avoid a dereference - lower pattern should give + * correct location so maybe change in future */ + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + // Constructor + RangePattern (std::unique_ptr lower, + std::unique_ptr upper, Location locus, + bool has_ellipsis_syntax = false) + : lower (std::move (lower)), upper (std::move (upper)), + has_ellipsis_syntax (has_ellipsis_syntax), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor with clone + RangePattern (RangePattern const &other) + : lower (other.lower->clone_range_pattern_bound ()), + upper (other.upper->clone_range_pattern_bound ()), + has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus), + node_id (other.node_id) + {} + + // Overloaded assignment operator to clone + RangePattern &operator= (RangePattern const &other) + { + lower = other.lower->clone_range_pattern_bound (); + upper = other.upper->clone_range_pattern_bound (); + has_ellipsis_syntax = other.has_ellipsis_syntax; + locus = other.locus; + node_id = other.node_id; + + return *this; + } + + // default move semantics + RangePattern (RangePattern &&other) = default; + RangePattern &operator= (RangePattern &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? or is a "vis_bound" better? + std::unique_ptr &get_lower_bound () + { + rust_assert (lower != nullptr); + return lower; + } + + std::unique_ptr &get_upper_bound () + { + rust_assert (upper != nullptr); + return upper; + } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RangePattern *clone_pattern_impl () const override + { + return new RangePattern (*this); + } +}; + +// AST node for pattern based on dereferencing the pointers given +class ReferencePattern : public Pattern +{ + bool has_two_amps; + bool is_mut; + std::unique_ptr pattern; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + ReferencePattern (std::unique_ptr pattern, bool is_mut_reference, + bool ref_has_two_amps, Location locus) + : has_two_amps (ref_has_two_amps), is_mut (is_mut_reference), + pattern (std::move (pattern)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor requires clone + ReferencePattern (ReferencePattern const &other) + : has_two_amps (other.has_two_amps), is_mut (other.is_mut), + pattern (other.pattern->clone_pattern ()), locus (other.locus), + node_id (other.node_id) + {} + + // Overload assignment operator to clone + ReferencePattern &operator= (ReferencePattern const &other) + { + pattern = other.pattern->clone_pattern (); + is_mut = other.is_mut; + has_two_amps = other.has_two_amps; + locus = other.locus; + node_id = other.node_id; + + return *this; + } + + // default move semantics + ReferencePattern (ReferencePattern &&other) = default; + ReferencePattern &operator= (ReferencePattern &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_referenced_pattern () + { + rust_assert (pattern != nullptr); + return pattern; + } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ReferencePattern *clone_pattern_impl () const override + { + return new ReferencePattern (*this); + } +}; + +#if 0 +// aka StructPatternEtCetera; potential element in struct pattern +struct StructPatternEtc +{ +private: + std::vector outer_attrs; + + // should this store location data? + +public: + StructPatternEtc (std::vector outer_attribs) + : outer_attrs (std::move (outer_attribs)) + {} + + // Creates an empty StructPatternEtc + static StructPatternEtc create_empty () + { + return StructPatternEtc (std::vector ()); + } +}; +#endif + +// Base class for a single field in a struct pattern - abstract +class StructPatternField +{ + std::vector outer_attrs; + Location locus; + +protected: + NodeId node_id; + +public: + enum ItemType + { + TUPLE_PAT, + IDENT_PAT, + IDENT + }; + + virtual ~StructPatternField () {} + + // Unique pointer custom clone function + std::unique_ptr clone_struct_pattern_field () const + { + return std::unique_ptr ( + clone_struct_pattern_field_impl ()); + } + + virtual std::string as_string () const; + + Location get_locus () const { return locus; } + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual void mark_for_strip () = 0; + virtual bool is_marked_for_strip () const = 0; + virtual ItemType get_item_type () const = 0; + + NodeId get_node_id () const { return node_id; } + + // TODO: seems kinda dodgy. Think of better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + +protected: + StructPatternField (std::vector outer_attribs, Location locus, + NodeId node_id) + : outer_attrs (std::move (outer_attribs)), locus (locus), node_id (node_id) + {} + + // Clone function implementation as pure virtual method + virtual StructPatternField *clone_struct_pattern_field_impl () const = 0; +}; + +// Tuple pattern single field in a struct pattern +class StructPatternFieldTuplePat : public StructPatternField +{ + TupleIndex index; + std::unique_ptr tuple_pattern; + +public: + StructPatternFieldTuplePat (TupleIndex index, + std::unique_ptr tuple_pattern, + std::vector outer_attribs, + Location locus) + : StructPatternField (std::move (outer_attribs), locus, + Analysis::Mappings::get ()->get_next_node_id ()), + index (index), tuple_pattern (std::move (tuple_pattern)) + {} + + // Copy constructor requires clone + StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other) + : StructPatternField (other), index (other.index) + { + // guard to prevent null dereference (only required if error state) + node_id = other.get_node_id (); + if (other.tuple_pattern != nullptr) + tuple_pattern = other.tuple_pattern->clone_pattern (); + } + + // Overload assignment operator to perform clone + StructPatternFieldTuplePat & + operator= (StructPatternFieldTuplePat const &other) + { + StructPatternField::operator= (other); + index = other.index; + // outer_attrs = other.outer_attrs; + node_id = other.get_node_id (); + + // guard to prevent null dereference (only required if error state) + if (other.tuple_pattern != nullptr) + tuple_pattern = other.tuple_pattern->clone_pattern (); + else + tuple_pattern = nullptr; + + return *this; + } + + // default move semantics + StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default; + StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // based on idea of tuple pattern no longer existing + void mark_for_strip () override { tuple_pattern = nullptr; } + bool is_marked_for_strip () const override + { + return tuple_pattern == nullptr; + } + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_index_pattern () + { + rust_assert (tuple_pattern != nullptr); + return tuple_pattern; + } + + ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override + { + return new StructPatternFieldTuplePat (*this); + } +}; + +// Identifier pattern single field in a struct pattern +class StructPatternFieldIdentPat : public StructPatternField +{ + Identifier ident; + std::unique_ptr ident_pattern; + +public: + StructPatternFieldIdentPat (Identifier ident, + std::unique_ptr ident_pattern, + std::vector outer_attrs, + Location locus) + : StructPatternField (std::move (outer_attrs), locus, + Analysis::Mappings::get ()->get_next_node_id ()), + ident (std::move (ident)), ident_pattern (std::move (ident_pattern)) + {} + + // Copy constructor requires clone + StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other) + : StructPatternField (other), ident (other.ident) + { + // guard to prevent null dereference (only required if error state) + node_id = other.get_node_id (); + if (other.ident_pattern != nullptr) + ident_pattern = other.ident_pattern->clone_pattern (); + } + + // Overload assignment operator to clone + StructPatternFieldIdentPat & + operator= (StructPatternFieldIdentPat const &other) + { + StructPatternField::operator= (other); + ident = other.ident; + // outer_attrs = other.outer_attrs; + node_id = other.get_node_id (); + + // guard to prevent null dereference (only required if error state) + if (other.ident_pattern != nullptr) + ident_pattern = other.ident_pattern->clone_pattern (); + else + ident_pattern = nullptr; + + return *this; + } + + // default move semantics + StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default; + StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // based on idea of identifier pattern no longer existing + void mark_for_strip () override { ident_pattern = nullptr; } + bool is_marked_for_strip () const override + { + return ident_pattern == nullptr; + } + + const Identifier &get_identifier () const { return ident; } + + // TODO: is this better? Or is a "vis_pattern" better? + std::unique_ptr &get_ident_pattern () + { + rust_assert (ident_pattern != nullptr); + return ident_pattern; + } + + ItemType get_item_type () const override final { return ItemType::IDENT_PAT; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override + { + return new StructPatternFieldIdentPat (*this); + } +}; + +// Identifier only (with no pattern) single field in a struct pattern +class StructPatternFieldIdent : public StructPatternField +{ + bool has_ref; + bool has_mut; + Identifier ident; + +public: + StructPatternFieldIdent (Identifier ident, bool is_ref, bool is_mut, + std::vector outer_attrs, Location locus) + : StructPatternField (std::move (outer_attrs), locus, + Analysis::Mappings::get ()->get_next_node_id ()), + has_ref (is_ref), has_mut (is_mut), ident (std::move (ident)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // based on idea of identifier no longer existing + void mark_for_strip () override { ident = {}; } + bool is_marked_for_strip () const override { return ident.empty (); } + + const Identifier &get_identifier () const { return ident; } + + ItemType get_item_type () const override final { return ItemType::IDENT; } + + bool is_ref () const { return has_ref; } + + bool is_mut () const { return has_mut; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructPatternFieldIdent *clone_struct_pattern_field_impl () const override + { + return new StructPatternFieldIdent (*this); + } +}; + +// Elements of a struct pattern +struct StructPatternElements +{ +private: + // bool has_struct_pattern_fields; + std::vector > fields; + + bool has_struct_pattern_etc; + std::vector struct_pattern_etc_attrs; + // StructPatternEtc etc; + + // must have at least one of the two and maybe both + + // should this store location data? + +public: + // Returns whether there are any struct pattern fields + bool has_struct_pattern_fields () const { return !fields.empty (); } + + /* Returns whether the struct pattern elements is entirely empty (no fields, + * no etc). */ + bool is_empty () const + { + return !has_struct_pattern_fields () && !has_struct_pattern_etc; + } + + bool has_etc () const { return has_struct_pattern_etc; } + + // Constructor for StructPatternElements with both (potentially) + StructPatternElements ( + std::vector > fields, + std::vector etc_attrs) + : fields (std::move (fields)), has_struct_pattern_etc (true), + struct_pattern_etc_attrs (std::move (etc_attrs)) + {} + + // Constructor for StructPatternElements with no StructPatternEtc + StructPatternElements ( + std::vector > fields) + : fields (std::move (fields)), has_struct_pattern_etc (false), + struct_pattern_etc_attrs () + {} + + // Copy constructor with vector clone + StructPatternElements (StructPatternElements const &other) + : has_struct_pattern_etc (other.has_struct_pattern_etc), + struct_pattern_etc_attrs (other.struct_pattern_etc_attrs) + { + fields.reserve (other.fields.size ()); + for (const auto &e : other.fields) + fields.push_back (e->clone_struct_pattern_field ()); + } + + // Overloaded assignment operator with vector clone + StructPatternElements &operator= (StructPatternElements const &other) + { + struct_pattern_etc_attrs = other.struct_pattern_etc_attrs; + has_struct_pattern_etc = other.has_struct_pattern_etc; + + fields.reserve (other.fields.size ()); + for (const auto &e : other.fields) + fields.push_back (e->clone_struct_pattern_field ()); + + return *this; + } + + // move constructors + StructPatternElements (StructPatternElements &&other) = default; + StructPatternElements &operator= (StructPatternElements &&other) = default; + + // Creates an empty StructPatternElements + static StructPatternElements create_empty () + { + return StructPatternElements ( + std::vector > ()); + } + + std::string as_string () const; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > & + get_struct_pattern_fields () + { + return fields; + } + const std::vector > & + get_struct_pattern_fields () const + { + return fields; + } + + std::vector &get_etc_outer_attrs () + { + return struct_pattern_etc_attrs; + } + const std::vector &get_etc_outer_attrs () const + { + return struct_pattern_etc_attrs; + } + + void strip_etc () + { + has_struct_pattern_etc = false; + struct_pattern_etc_attrs.clear (); + struct_pattern_etc_attrs.shrink_to_fit (); + } +}; + +// Struct pattern AST node representation +class StructPattern : public Pattern +{ + PathInExpression path; + + // bool has_struct_pattern_elements; + StructPatternElements elems; + + NodeId node_id; + Location locus; + +public: + std::string as_string () const override; + + // Constructs a struct pattern from specified StructPatternElements + StructPattern (PathInExpression struct_path, Location locus, + StructPatternElements elems + = StructPatternElements::create_empty ()) + : path (std::move (struct_path)), elems (std::move (elems)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + /* TODO: constructor to construct via elements included in + * StructPatternElements */ + + /* Returns whether struct pattern has any struct pattern elements (if not, it + * is empty). */ + bool has_struct_pattern_elems () const { return !elems.is_empty (); } + + Location get_locus () const { return path.get_locus (); } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + StructPatternElements &get_struct_pattern_elems () { return elems; } + const StructPatternElements &get_struct_pattern_elems () const + { + return elems; + } + + PathInExpression &get_path () { return path; } + const PathInExpression &get_path () const { return path; } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + StructPattern *clone_pattern_impl () const override + { + return new StructPattern (*this); + } +}; + +// Base abstract class for patterns used in TupleStructPattern +class TupleStructItems +{ +public: + enum ItemType + { + RANGE, + NO_RANGE + }; + + virtual ~TupleStructItems () {} + + // TODO: should this store location data? + + // Unique pointer custom clone function + std::unique_ptr clone_tuple_struct_items () const + { + return std::unique_ptr (clone_tuple_struct_items_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual ItemType get_item_type () const = 0; + +protected: + // pure virtual clone implementation + virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0; +}; + +// Class for non-ranged tuple struct pattern patterns +class TupleStructItemsNoRange : public TupleStructItems +{ + std::vector > patterns; + +public: + TupleStructItemsNoRange (std::vector > patterns) + : patterns (std::move (patterns)) + {} + + // Copy constructor with vector clone + TupleStructItemsNoRange (TupleStructItemsNoRange const &other) + { + patterns.reserve (other.patterns.size ()); + for (const auto &e : other.patterns) + patterns.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator with vector clone + TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other) + { + patterns.reserve (other.patterns.size ()); + for (const auto &e : other.patterns) + patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default; + TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_patterns () { return patterns; } + const std::vector > &get_patterns () const + { + return patterns; + } + + ItemType get_item_type () const override final { return ItemType::NO_RANGE; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override + { + return new TupleStructItemsNoRange (*this); + } +}; + +// Class for ranged tuple struct pattern patterns +class TupleStructItemsRange : public TupleStructItems +{ + std::vector > lower_patterns; + std::vector > upper_patterns; + +public: + TupleStructItemsRange (std::vector > lower_patterns, + std::vector > upper_patterns) + : lower_patterns (std::move (lower_patterns)), + upper_patterns (std::move (upper_patterns)) + {} + + // Copy constructor with vector clone + TupleStructItemsRange (TupleStructItemsRange const &other) + { + lower_patterns.reserve (other.lower_patterns.size ()); + for (const auto &e : other.lower_patterns) + lower_patterns.push_back (e->clone_pattern ()); + + upper_patterns.reserve (other.upper_patterns.size ()); + for (const auto &e : other.upper_patterns) + upper_patterns.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator to clone + TupleStructItemsRange &operator= (TupleStructItemsRange const &other) + { + lower_patterns.reserve (other.lower_patterns.size ()); + for (const auto &e : other.lower_patterns) + lower_patterns.push_back (e->clone_pattern ()); + + upper_patterns.reserve (other.upper_patterns.size ()); + for (const auto &e : other.upper_patterns) + upper_patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + TupleStructItemsRange (TupleStructItemsRange &&other) = default; + TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_lower_patterns () + { + return lower_patterns; + } + const std::vector > &get_lower_patterns () const + { + return lower_patterns; + } + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_upper_patterns () + { + return upper_patterns; + } + const std::vector > &get_upper_patterns () const + { + return upper_patterns; + } + + ItemType get_item_type () const override final { return ItemType::RANGE; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleStructItemsRange *clone_tuple_struct_items_impl () const override + { + return new TupleStructItemsRange (*this); + } +}; + +// AST node representing a tuple struct pattern +class TupleStructPattern : public Pattern +{ + PathInExpression path; + std::unique_ptr items; + NodeId node_id; + + /* TOOD: should this store location data? current accessor uses path location + * data */ + +public: + std::string as_string () const override; + + // Returns whether the pattern has tuple struct items. + bool has_items () const { return items != nullptr; } + + TupleStructPattern (PathInExpression tuple_struct_path, + std::unique_ptr items) + : path (std::move (tuple_struct_path)), items (std::move (items)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor required to clone + TupleStructPattern (TupleStructPattern const &other) : path (other.path) + { + // guard to protect from null dereference + node_id = other.node_id; + if (other.items != nullptr) + items = other.items->clone_tuple_struct_items (); + } + + // Operator overload assignment operator to clone + TupleStructPattern &operator= (TupleStructPattern const &other) + { + path = other.path; + node_id = other.node_id; + + // guard to protect from null dereference + if (other.items != nullptr) + items = other.items->clone_tuple_struct_items (); + else + items = nullptr; + + return *this; + } + + // move constructors + TupleStructPattern (TupleStructPattern &&other) = default; + TupleStructPattern &operator= (TupleStructPattern &&other) = default; + + Location get_locus () const override { return path.get_locus (); } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::unique_ptr &get_items () + { + rust_assert (has_items ()); + return items; + } + + PathInExpression &get_path () { return path; } + const PathInExpression &get_path () const { return path; } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleStructPattern *clone_pattern_impl () const override + { + return new TupleStructPattern (*this); + } +}; + +// Base abstract class representing TuplePattern patterns +class TuplePatternItems +{ +public: + enum TuplePatternItemType + { + MULTIPLE, + RANGED, + }; + + virtual ~TuplePatternItems () {} + + // TODO: should this store location data? + + // Unique pointer custom clone function + std::unique_ptr clone_tuple_pattern_items () const + { + return std::unique_ptr ( + clone_tuple_pattern_items_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual TuplePatternItemType get_pattern_type () const = 0; + +protected: + // pure virtual clone implementation + virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0; +}; + +// Class representing TuplePattern patterns where there is only a single pattern +/*class TuplePatternItemsSingle : public TuplePatternItems { + // Pattern pattern; + std::unique_ptr pattern; + + public: + TuplePatternItemsSingle(Pattern* pattern) : pattern(pattern) {} + + // Copy constructor uses clone + TuplePatternItemsSingle(TuplePatternItemsSingle const& other) : + pattern(other.pattern->clone_pattern()) {} + + // Destructor - define here if required + + // Overload assignment operator to clone + TuplePatternItemsSingle& operator=(TuplePatternItemsSingle const& other) { + pattern = other.pattern->clone_pattern(); + + return *this; + } + + // move constructors + TuplePatternItemsSingle(TuplePatternItemsSingle&& other) = default; + TuplePatternItemsSingle& operator=(TuplePatternItemsSingle&& other) = +default; + + protected: + // Use covariance to implement clone function as returning this object +rather than base virtual TuplePatternItemsSingle* +clone_tuple_pattern_items_impl() const override { return new +TuplePatternItemsSingle(*this); + } +};*/ +// removed in favour of single-element TuplePatternItemsMultiple + +// Class representing TuplePattern patterns where there are multiple patterns +class TuplePatternItemsMultiple : public TuplePatternItems +{ + std::vector > patterns; + +public: + TuplePatternItemsMultiple (std::vector > patterns) + : patterns (std::move (patterns)) + {} + + // Copy constructor with vector clone + TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other) + { + patterns.reserve (other.patterns.size ()); + for (const auto &e : other.patterns) + patterns.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator to vector clone + TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other) + { + patterns.reserve (other.patterns.size ()); + for (const auto &e : other.patterns) + patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default; + TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_patterns () { return patterns; } + const std::vector > &get_patterns () const + { + return patterns; + } + + TuplePatternItemType get_pattern_type () const override + { + return TuplePatternItemType::MULTIPLE; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override + { + return new TuplePatternItemsMultiple (*this); + } +}; + +// Class representing TuplePattern patterns where there are a range of patterns +class TuplePatternItemsRanged : public TuplePatternItems +{ + std::vector > lower_patterns; + std::vector > upper_patterns; + +public: + TuplePatternItemsRanged ( + std::vector > lower_patterns, + std::vector > upper_patterns) + : lower_patterns (std::move (lower_patterns)), + upper_patterns (std::move (upper_patterns)) + {} + + // Copy constructor with vector clone + TuplePatternItemsRanged (TuplePatternItemsRanged const &other) + { + lower_patterns.reserve (other.lower_patterns.size ()); + for (const auto &e : other.lower_patterns) + lower_patterns.push_back (e->clone_pattern ()); + + upper_patterns.reserve (other.upper_patterns.size ()); + for (const auto &e : other.upper_patterns) + upper_patterns.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator to clone + TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other) + { + lower_patterns.reserve (other.lower_patterns.size ()); + for (const auto &e : other.lower_patterns) + lower_patterns.push_back (e->clone_pattern ()); + + upper_patterns.reserve (other.upper_patterns.size ()); + for (const auto &e : other.upper_patterns) + upper_patterns.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default; + TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_lower_patterns () + { + return lower_patterns; + } + const std::vector > &get_lower_patterns () const + { + return lower_patterns; + } + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_upper_patterns () + { + return upper_patterns; + } + const std::vector > &get_upper_patterns () const + { + return upper_patterns; + } + + TuplePatternItemType get_pattern_type () const override + { + return TuplePatternItemType::RANGED; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override + { + return new TuplePatternItemsRanged (*this); + } +}; + +// AST node representing a tuple pattern +class TuplePattern : public Pattern +{ + // bool has_tuple_pattern_items; + std::unique_ptr items; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + // Returns true if the tuple pattern has items + bool has_tuple_pattern_items () const { return items != nullptr; } + + TuplePattern (std::unique_ptr items, Location locus) + : items (std::move (items)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor requires clone + TuplePattern (TuplePattern const &other) : locus (other.locus) + { + // guard to prevent null dereference + node_id = other.node_id; + if (other.items != nullptr) + items = other.items->clone_tuple_pattern_items (); + } + + // Overload assignment operator to clone + TuplePattern &operator= (TuplePattern const &other) + { + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.items != nullptr) + items = other.items->clone_tuple_pattern_items (); + else + items = nullptr; + + return *this; + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::unique_ptr &get_items () + { + rust_assert (has_tuple_pattern_items ()); + return items; + } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TuplePattern *clone_pattern_impl () const override + { + return new TuplePattern (*this); + } +}; + +// AST node representing a pattern in parentheses, used to control precedence +class GroupedPattern : public Pattern +{ + std::unique_ptr pattern_in_parens; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override + { + return "(" + pattern_in_parens->as_string () + ")"; + } + + GroupedPattern (std::unique_ptr pattern_in_parens, Location locus) + : pattern_in_parens (std::move (pattern_in_parens)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor uses clone + GroupedPattern (GroupedPattern const &other) + : pattern_in_parens (other.pattern_in_parens->clone_pattern ()), + locus (other.locus), node_id (other.node_id) + {} + + // Overload assignment operator to clone + GroupedPattern &operator= (GroupedPattern const &other) + { + pattern_in_parens = other.pattern_in_parens->clone_pattern (); + locus = other.locus; + node_id = other.node_id; + + return *this; + } + + // default move semantics + GroupedPattern (GroupedPattern &&other) = default; + GroupedPattern &operator= (GroupedPattern &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::unique_ptr &get_pattern_in_parens () + { + rust_assert (pattern_in_parens != nullptr); + return pattern_in_parens; + } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + GroupedPattern *clone_pattern_impl () const override + { + return new GroupedPattern (*this); + } +}; + +// AST node representing patterns that can match slices and arrays +class SlicePattern : public Pattern +{ + std::vector > items; + Location locus; + NodeId node_id; + +public: + std::string as_string () const override; + + SlicePattern (std::vector > items, Location locus) + : items (std::move (items)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor with vector clone + SlicePattern (SlicePattern const &other) : locus (other.locus) + { + node_id = other.node_id; + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_pattern ()); + } + + // Overloaded assignment operator to vector clone + SlicePattern &operator= (SlicePattern const &other) + { + locus = other.locus; + node_id = other.node_id; + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_pattern ()); + + return *this; + } + + // move constructors + SlicePattern (SlicePattern &&other) = default; + SlicePattern &operator= (SlicePattern &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: seems kinda dodgy. Think of better way. + std::vector > &get_items () { return items; } + const std::vector > &get_items () const + { + return items; + } + + NodeId get_node_id () const { return node_id; } + + NodeId get_pattern_node_id () const override final { return node_id; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + SlicePattern *clone_pattern_impl () const override + { + return new SlicePattern (*this); + } +}; + +// Moved definition to rust-path.h +class PathPattern; + +// Forward decls for paths (defined in rust-path.h) +class PathInExpression; +class QualifiedPathInExpression; + +// Replaced with forward decl - defined in rust-macro.h +class MacroInvocation; +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h new file mode 100644 index 00000000000..9d95c3e27e8 --- /dev/null +++ b/gcc/rust/ast/rust-stmt.h @@ -0,0 +1,358 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_STATEMENT_H +#define RUST_AST_STATEMENT_H + +#include "rust-ast.h" +#include "rust-path.h" +#include "rust-expr.h" + +namespace Rust { +namespace AST { +// Just a semi-colon, which apparently is a statement. +class EmptyStmt : public Stmt +{ + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override { return std::string (1, ';'); } + + EmptyStmt (Location locus) : locus (locus) {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + bool is_item () const override final { return false; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + EmptyStmt *clone_stmt_impl () const override { return new EmptyStmt (*this); } +}; + +/* Variable assignment let statement - type of "declaration statement" as it + * introduces new name into scope */ +class LetStmt : public Stmt +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + std::unique_ptr variables_pattern; + + // bool has_type; + std::unique_ptr type; + + // bool has_init_expr; + std::unique_ptr init_expr; + + Location locus; + +public: + Type *inferedType; + + // Returns whether let statement has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether let statement has a given return type. + bool has_type () const { return type != nullptr; } + + // Returns whether let statement has an initialisation expression. + bool has_init_expr () const { return init_expr != nullptr; } + + std::string as_string () const override; + + LetStmt (std::unique_ptr variables_pattern, + std::unique_ptr init_expr, std::unique_ptr type, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), + variables_pattern (std::move (variables_pattern)), + type (std::move (type)), init_expr (std::move (init_expr)), locus (locus) + {} + + // Copy constructor with clone + LetStmt (LetStmt const &other) + : outer_attrs (other.outer_attrs), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.variables_pattern != nullptr) + variables_pattern = other.variables_pattern->clone_pattern (); + + // guard to prevent null dereference (always required) + if (other.init_expr != nullptr) + init_expr = other.init_expr->clone_expr (); + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overloaded assignment operator to clone + LetStmt &operator= (LetStmt const &other) + { + outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.variables_pattern != nullptr) + variables_pattern = other.variables_pattern->clone_pattern (); + else + variables_pattern = nullptr; + + // guard to prevent null dereference (always required) + if (other.init_expr != nullptr) + init_expr = other.init_expr->clone_expr (); + else + init_expr = nullptr; + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + LetStmt (LetStmt &&other) = default; + LetStmt &operator= (LetStmt &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if pattern is null, so base stripping on that. + void mark_for_strip () override { variables_pattern = nullptr; } + bool is_marked_for_strip () const override + { + return variables_pattern == nullptr; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_init_expr () + { + rust_assert (has_init_expr ()); + return init_expr; + } + + std::unique_ptr &get_pattern () + { + rust_assert (variables_pattern != nullptr); + return variables_pattern; + } + + std::unique_ptr &get_type () + { + rust_assert (has_type ()); + return type; + } + + bool is_item () const override final { return false; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + LetStmt *clone_stmt_impl () const override { return new LetStmt (*this); } +}; + +/* Abstract base class for expression statements (statements containing an + * expression) */ +class ExprStmt : public Stmt +{ +public: + enum ExprStmtType + { + WITH_BLOCK, + WITHOUT_BLOCK + }; + +protected: + Location locus; + +public: + Location get_locus () const override final { return locus; } + + bool is_item () const override final { return false; } + + virtual ExprStmtType get_type () const = 0; + +protected: + ExprStmt (Location locus) : locus (locus) {} +}; + +/* Statement containing an expression without a block (or, due to technical + * difficulties, can only be guaranteed to hold an expression). */ +class ExprStmtWithoutBlock : public ExprStmt +{ + // TODO: ensure that this works + std::unique_ptr expr; + /* HACK: cannot ensure type safety of ExprWithoutBlock due to Pratt parsing, + * so have to store more general type of Expr. FIXME: fix this issue somehow + * or redesign AST. */ + // std::unique_ptr expr; + +public: + std::string as_string () const override; + + ExprStmtWithoutBlock (std::unique_ptr expr, Location locus) + : ExprStmt (locus), expr (std::move (expr->to_stmt ())) + {} + + /*ExprStmtWithoutBlock (std::unique_ptr expr, Location locus) + : ExprStmt (locus), expr (std::move (expr)) + {}*/ + + // Copy constructor with clone + ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other) : ExprStmt (other) + { + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_expr_without_block (); + } + /*ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other) + : ExprStmt (other), expr (other.expr->clone_expr ()) + {}*/ + + // Overloaded assignment operator to clone + ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock const &other) + { + ExprStmt::operator= (other); + // expr = other.expr->clone_expr (); + + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_expr_without_block (); + else + expr = nullptr; + + return *this; + } + + // move constructors + ExprStmtWithoutBlock (ExprStmtWithoutBlock &&other) = default; + ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { expr = nullptr; } + bool is_marked_for_strip () const override { return expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + ExprStmtType get_type () const override + { + return ExprStmtType::WITHOUT_BLOCK; + }; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ExprStmtWithoutBlock *clone_stmt_impl () const override + { + return new ExprStmtWithoutBlock (*this); + } +}; + +// Statement containing an expression with a block +class ExprStmtWithBlock : public ExprStmt +{ + std::unique_ptr expr; + bool semicolon_followed; + +public: + std::string as_string () const override; + + std::vector locals; + + ExprStmtWithBlock (std::unique_ptr expr, Location locus, + bool semicolon_followed) + : ExprStmt (locus), expr (std::move (expr)), + semicolon_followed (semicolon_followed) + {} + + // Copy constructor with clone + ExprStmtWithBlock (ExprStmtWithBlock const &other) : ExprStmt (other) + { + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_expr_with_block (); + } + + // Overloaded assignment operator to clone + ExprStmtWithBlock &operator= (ExprStmtWithBlock const &other) + { + ExprStmt::operator= (other); + + // guard to prevent null dereference (only required if error state) + if (other.expr != nullptr) + expr = other.expr->clone_expr_with_block (); + else + expr = nullptr; + + return *this; + } + + // move constructors + ExprStmtWithBlock (ExprStmtWithBlock &&other) = default; + ExprStmtWithBlock &operator= (ExprStmtWithBlock &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if expr is null, so base stripping on that. + void mark_for_strip () override { expr = nullptr; } + bool is_marked_for_strip () const override { return expr == nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + bool is_semicolon_followed () const { return semicolon_followed; } + + ExprStmtType get_type () const override { return ExprStmtType::WITH_BLOCK; }; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ExprStmtWithBlock *clone_stmt_impl () const override + { + return new ExprStmtWithBlock (*this); + } +}; + +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/ast/rust-type.h b/gcc/rust/ast/rust-type.h new file mode 100644 index 00000000000..7e9e07d0c18 --- /dev/null +++ b/gcc/rust/ast/rust-type.h @@ -0,0 +1,962 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AST_TYPE_H +#define RUST_AST_TYPE_H + +#include "rust-ast.h" +#include "rust-path.h" + +namespace Rust { +namespace AST { +// definitions moved to rust-ast.h +class TypeParamBound; +class Lifetime; + +// A trait bound +class TraitBound : public TypeParamBound +{ + bool in_parens; + bool opening_question_mark; + + // bool has_for_lifetimes; + // LifetimeParams for_lifetimes; + std::vector for_lifetimes; // inlined LifetimeParams + + TypePath type_path; + + Location locus; + +public: + // Returns whether trait bound has "for" lifetimes + bool has_for_lifetimes () const { return !for_lifetimes.empty (); } + + TraitBound (TypePath type_path, Location locus, bool in_parens = false, + bool opening_question_mark = false, + std::vector for_lifetimes + = std::vector ()) + : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()), + in_parens (in_parens), opening_question_mark (opening_question_mark), + for_lifetimes (std::move (for_lifetimes)), + type_path (std::move (type_path)), locus (locus) + {} + + TraitBound (NodeId id, TypePath type_path, Location locus, + bool in_parens = false, bool opening_question_mark = false, + std::vector for_lifetimes + = std::vector ()) + : TypeParamBound (id), in_parens (in_parens), + opening_question_mark (opening_question_mark), + for_lifetimes (std::move (for_lifetimes)), + type_path (std::move (type_path)), locus (locus) + {} + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems kinda dodgy + TypePath &get_type_path () { return type_path; } + const TypePath &get_type_path () const { return type_path; } + + bool is_in_parens () const { return in_parens; } + bool has_opening_question_mark () const { return opening_question_mark; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TraitBound *clone_type_param_bound_impl () const override + { + return new TraitBound (node_id, type_path, locus, in_parens, + opening_question_mark, for_lifetimes); + } +}; + +// definition moved to rust-ast.h +class TypeNoBounds; + +// An impl trait? Poor reference material here. +class ImplTraitType : public Type +{ + // TypeParamBounds type_param_bounds; + // inlined form + std::vector > type_param_bounds; + + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ImplTraitType *clone_type_impl () const override + { + return new ImplTraitType (*this); + } + +public: + ImplTraitType ( + std::vector > type_param_bounds, + Location locus) + : type_param_bounds (std::move (type_param_bounds)), locus (locus) + {} + + // copy constructor with vector clone + ImplTraitType (ImplTraitType const &other) : locus (other.locus) + { + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // overloaded assignment operator to clone + ImplTraitType &operator= (ImplTraitType const &other) + { + locus = other.locus; + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + ImplTraitType (ImplTraitType &&other) = default; + ImplTraitType &operator= (ImplTraitType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: mutable getter seems kinda dodgy + std::vector > &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector > & + get_type_param_bounds () const + { + return type_param_bounds; + } +}; + +// An opaque value of another type that implements a set of traits +class TraitObjectType : public Type +{ + bool has_dyn; + std::vector > type_param_bounds; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TraitObjectType *clone_type_impl () const override + { + return new TraitObjectType (*this); + } + +public: + TraitObjectType ( + std::vector > type_param_bounds, + Location locus, bool is_dyn_dispatch) + : has_dyn (is_dyn_dispatch), + type_param_bounds (std::move (type_param_bounds)), locus (locus) + {} + + // copy constructor with vector clone + TraitObjectType (TraitObjectType const &other) + : has_dyn (other.has_dyn), locus (other.locus) + { + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // overloaded assignment operator to clone + TraitObjectType &operator= (TraitObjectType const &other) + { + has_dyn = other.has_dyn; + locus = other.locus; + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + TraitObjectType (TraitObjectType &&other) = default; + TraitObjectType &operator= (TraitObjectType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + bool is_dyn () const { return has_dyn; } + + // TODO: mutable getter seems kinda dodgy + std::vector > &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector > & + get_type_param_bounds () const + { + return type_param_bounds; + } +}; + +// A type with parentheses around it, used to avoid ambiguity. +class ParenthesisedType : public TypeNoBounds +{ + std::unique_ptr type_in_parens; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ParenthesisedType *clone_type_no_bounds_impl () const override + { + return new ParenthesisedType (*this); + } + +public: + // Constructor uses Type pointer for polymorphism + ParenthesisedType (std::unique_ptr type_inside_parens, Location locus) + : type_in_parens (std::move (type_inside_parens)), locus (locus) + {} + + /* Copy constructor uses custom deep copy method for type to preserve + * polymorphism */ + ParenthesisedType (ParenthesisedType const &other) + : type_in_parens (other.type_in_parens->clone_type ()), locus (other.locus) + {} + + // overload assignment operator to use custom clone method + ParenthesisedType &operator= (ParenthesisedType const &other) + { + type_in_parens = other.type_in_parens->clone_type (); + locus = other.locus; + return *this; + } + + // default move semantics + ParenthesisedType (ParenthesisedType &&other) = default; + ParenthesisedType &operator= (ParenthesisedType &&other) = default; + + std::string as_string () const override + { + return "(" + type_in_parens->as_string () + ")"; + } + + // Creates a trait bound (clone of this one's trait bound) - HACK + TraitBound *to_trait_bound (bool) const override + { + /* NOTE: obviously it is unknown whether the internal type is a trait bound + * due to polymorphism, so just let the internal type handle it. As + * parenthesised type, it must be in parentheses. */ + return type_in_parens->to_trait_bound (true); + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_type_in_parens () + { + rust_assert (type_in_parens != nullptr); + return type_in_parens; + } +}; + +// Impl trait with a single bound? Poor reference material here. +class ImplTraitTypeOneBound : public TypeNoBounds +{ + TraitBound trait_bound; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override + { + return new ImplTraitTypeOneBound (*this); + } + +public: + ImplTraitTypeOneBound (TraitBound trait_bound, Location locus) + : trait_bound (std::move (trait_bound)), locus (locus) + {} + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + TraitBound &get_trait_bound () + { + // TODO: check to ensure invariants are met? + return trait_bound; + } +}; + +/* A trait object with a single trait bound. The "trait bound" is really just + * the trait. Basically like using an interface as a type in an OOP language. */ +class TraitObjectTypeOneBound : public TypeNoBounds +{ + bool has_dyn; + TraitBound trait_bound; + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TraitObjectTypeOneBound *clone_type_no_bounds_impl () const override + { + return new TraitObjectTypeOneBound (*this); + } + +public: + TraitObjectTypeOneBound (TraitBound trait_bound, Location locus, + bool is_dyn_dispatch = false) + : has_dyn (is_dyn_dispatch), trait_bound (std::move (trait_bound)), + locus (locus) + {} + + std::string as_string () const override; + + // Creates a trait bound (clone of this one's trait bound) - HACK + TraitBound *to_trait_bound (bool) const override + { + /* NOTE: this assumes there is no dynamic dispatch specified- if there was, + * this cloning would not be required as parsing is unambiguous. */ + return new TraitBound (trait_bound); + } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + TraitBound &get_trait_bound () + { + // TODO: check to ensure invariants are met? + return trait_bound; + } + + bool is_dyn () const { return has_dyn; } +}; + +class TypePath; // definition moved to "rust-path.h" + +/* A type consisting of the "product" of others (the tuple's elements) in a + * specific order */ +class TupleType : public TypeNoBounds +{ + std::vector > elems; + Location locus; + +public: + // Returns whether the tuple type is the unit type, i.e. has no elements. + bool is_unit_type () const { return elems.empty (); } + + TupleType (std::vector > elems, Location locus) + : elems (std::move (elems)), locus (locus) + {} + + // copy constructor with vector clone + TupleType (TupleType const &other) : locus (other.locus) + { + elems.reserve (other.elems.size ()); + for (const auto &e : other.elems) + elems.push_back (e->clone_type ()); + } + + // overloaded assignment operator to clone + TupleType &operator= (TupleType const &other) + { + locus = other.locus; + + elems.reserve (other.elems.size ()); + for (const auto &e : other.elems) + elems.push_back (e->clone_type ()); + + return *this; + } + + // move constructors + TupleType (TupleType &&other) = default; + TupleType &operator= (TupleType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: mutable getter seems kinda dodgy + std::vector > &get_elems () { return elems; } + const std::vector > &get_elems () const + { + return elems; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + TupleType *clone_type_no_bounds_impl () const override + { + return new TupleType (*this); + } +}; + +/* A type with no values, representing the result of computations that never + * complete. Expressions of NeverType can be coerced into any other types. + * Represented as "!". */ +class NeverType : public TypeNoBounds +{ + Location locus; + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + NeverType *clone_type_no_bounds_impl () const override + { + return new NeverType (*this); + } + +public: + NeverType (Location locus) : locus (locus) {} + + std::string as_string () const override { return "! (never type)"; } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; +}; + +// A type consisting of a pointer without safety or liveness guarantees +class RawPointerType : public TypeNoBounds +{ +public: + enum PointerType + { + MUT, + CONST + }; + +private: + PointerType pointer_type; + std::unique_ptr type; + Location locus; + +public: + // Returns whether the pointer is mutable or constant. + PointerType get_pointer_type () const { return pointer_type; } + + // Constructor requires pointer for polymorphism reasons + RawPointerType (PointerType pointer_type, + std::unique_ptr type_no_bounds, Location locus) + : pointer_type (pointer_type), type (std::move (type_no_bounds)), + locus (locus) + {} + + // Copy constructor calls custom polymorphic clone function + RawPointerType (RawPointerType const &other) + : pointer_type (other.pointer_type), + type (other.type->clone_type_no_bounds ()), locus (other.locus) + {} + + // overload assignment operator to use custom clone method + RawPointerType &operator= (RawPointerType const &other) + { + pointer_type = other.pointer_type; + type = other.type->clone_type_no_bounds (); + locus = other.locus; + return *this; + } + + // default move semantics + RawPointerType (RawPointerType &&other) = default; + RawPointerType &operator= (RawPointerType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_type_pointed_to () + { + rust_assert (type != nullptr); + return type; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + RawPointerType *clone_type_no_bounds_impl () const override + { + return new RawPointerType (*this); + } +}; + +// A type pointing to memory owned by another value +class ReferenceType : public TypeNoBounds +{ + // bool has_lifetime; // TODO: handle in lifetime or something? + Lifetime lifetime; + + bool has_mut; + std::unique_ptr type; + Location locus; + +public: + // Returns whether the reference is mutable or immutable. + bool is_mut () const { return has_mut; } + + // Returns whether the reference has a lifetime. + bool has_lifetime () const { return !lifetime.is_error (); } + + // Constructor + ReferenceType (bool is_mut, std::unique_ptr type_no_bounds, + Location locus, Lifetime lifetime = Lifetime::error ()) + : lifetime (std::move (lifetime)), has_mut (is_mut), + type (std::move (type_no_bounds)), locus (locus) + {} + + // Copy constructor with custom clone method + ReferenceType (ReferenceType const &other) + : lifetime (other.lifetime), has_mut (other.has_mut), + type (other.type->clone_type_no_bounds ()), locus (other.locus) + {} + + // Operator overload assignment operator to custom clone the unique pointer + ReferenceType &operator= (ReferenceType const &other) + { + lifetime = other.lifetime; + has_mut = other.has_mut; + type = other.type->clone_type_no_bounds (); + locus = other.locus; + + return *this; + } + + // move constructors + ReferenceType (ReferenceType &&other) = default; + ReferenceType &operator= (ReferenceType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_type_referenced () + { + rust_assert (type != nullptr); + return type; + } + + bool get_has_mut () const { return has_mut; } + + Lifetime &get_lifetime () { return lifetime; } + + std::unique_ptr &get_base_type () { return type; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ReferenceType *clone_type_no_bounds_impl () const override + { + return new ReferenceType (*this); + } +}; + +// A fixed-size sequence of elements of a specified type +class ArrayType : public TypeNoBounds +{ + std::unique_ptr elem_type; + std::unique_ptr size; + Location locus; + +public: + // Constructor requires pointers for polymorphism + ArrayType (std::unique_ptr type, std::unique_ptr array_size, + Location locus) + : elem_type (std::move (type)), size (std::move (array_size)), locus (locus) + {} + + // Copy constructor requires deep copies of both unique pointers + ArrayType (ArrayType const &other) + : elem_type (other.elem_type->clone_type ()), + size (other.size->clone_expr ()), locus (other.locus) + {} + + // Overload assignment operator to deep copy pointers + ArrayType &operator= (ArrayType const &other) + { + elem_type = other.elem_type->clone_type (); + size = other.size->clone_expr (); + locus = other.locus; + return *this; + } + + // move constructors + ArrayType (ArrayType &&other) = default; + ArrayType &operator= (ArrayType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_elem_type () + { + rust_assert (elem_type != nullptr); + return elem_type; + } + + // TODO: would a "vis_expr" be better? + std::unique_ptr &get_size_expr () + { + rust_assert (size != nullptr); + return size; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + ArrayType *clone_type_no_bounds_impl () const override + { + return new ArrayType (*this); + } +}; + +/* A dynamically-sized type representing a "view" into a sequence of elements of + * a type */ +class SliceType : public TypeNoBounds +{ + std::unique_ptr elem_type; + Location locus; + +public: + // Constructor requires pointer for polymorphism + SliceType (std::unique_ptr type, Location locus) + : elem_type (std::move (type)), locus (locus) + {} + + // Copy constructor requires deep copy of Type smart pointer + SliceType (SliceType const &other) + : elem_type (other.elem_type->clone_type ()), locus (other.locus) + {} + + // Overload assignment operator to deep copy + SliceType &operator= (SliceType const &other) + { + elem_type = other.elem_type->clone_type (); + locus = other.locus; + + return *this; + } + + // move constructors + SliceType (SliceType &&other) = default; + SliceType &operator= (SliceType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_elem_type () + { + rust_assert (elem_type != nullptr); + return elem_type; + } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + SliceType *clone_type_no_bounds_impl () const override + { + return new SliceType (*this); + } +}; + +/* Type used in generic arguments to explicitly request type inference (wildcard + * pattern) */ +class InferredType : public TypeNoBounds +{ + Location locus; + + // e.g. Vec<_> = whatever +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + InferredType *clone_type_no_bounds_impl () const override + { + return new InferredType (*this); + } + +public: + InferredType (Location locus) : locus (locus) {} + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; +}; + +class QualifiedPathInType; // definition moved to "rust-path.h" + +// A possibly named param used in a BaseFunctionType +struct MaybeNamedParam +{ +public: + enum ParamKind + { + UNNAMED, + IDENTIFIER, + WILDCARD + }; + +private: + std::vector outer_attrs; + + std::unique_ptr param_type; + + ParamKind param_kind; + Identifier name; // technically, can be an identifier or '_' + + Location locus; + +public: + MaybeNamedParam (Identifier name, ParamKind param_kind, + std::unique_ptr param_type, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), + param_type (std::move (param_type)), param_kind (param_kind), + name (std::move (name)), locus (locus) + {} + + // Copy constructor with clone + MaybeNamedParam (MaybeNamedParam const &other) + : outer_attrs (other.outer_attrs), param_kind (other.param_kind), + name (other.name), locus (other.locus) + { + // guard to prevent null dereference + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + } + + ~MaybeNamedParam () = default; + + // Overloaded assignment operator with clone + MaybeNamedParam &operator= (MaybeNamedParam const &other) + { + outer_attrs = other.outer_attrs; + name = other.name; + param_kind = other.param_kind; + locus = other.locus; + + // guard to prevent null dereference + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + else + param_type = nullptr; + + return *this; + } + + // move constructors + MaybeNamedParam (MaybeNamedParam &&other) = default; + MaybeNamedParam &operator= (MaybeNamedParam &&other) = default; + + std::string as_string () const; + + // Returns whether the param is in an error state. + bool is_error () const { return param_type == nullptr; } + + // Creates an error state param. + static MaybeNamedParam create_error () + { + return MaybeNamedParam ("", UNNAMED, nullptr, {}, Location ()); + } + + Location get_locus () const { return locus; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_type () + { + rust_assert (param_type != nullptr); + return param_type; + } + + ParamKind get_param_kind () const { return param_kind; } + + Identifier get_name () const { return name; } +}; + +/* A function pointer type - can be created via coercion from function items and + * non- capturing closures. */ +class BareFunctionType : public TypeNoBounds +{ + // bool has_for_lifetimes; + // ForLifetimes for_lifetimes; + std::vector for_lifetimes; // inlined version + + FunctionQualifiers function_qualifiers; + std::vector params; + bool is_variadic; + std::vector variadic_attrs; + + // bool has_return_type; + // BareFunctionReturnType return_type; + std::unique_ptr return_type; // inlined version + + Location locus; + +public: + // Whether a return type is defined with the function. + bool has_return_type () const { return return_type != nullptr; } + + // Whether the function has ForLifetimes. + bool has_for_lifetimes () const { return !for_lifetimes.empty (); } + + BareFunctionType (std::vector lifetime_params, + FunctionQualifiers qualifiers, + std::vector named_params, bool is_variadic, + std::vector variadic_attrs, + std::unique_ptr type, Location locus) + : for_lifetimes (std::move (lifetime_params)), + function_qualifiers (std::move (qualifiers)), + params (std::move (named_params)), is_variadic (is_variadic), + variadic_attrs (std::move (variadic_attrs)), + return_type (std::move (type)), locus (locus) + { + if (!variadic_attrs.empty ()) + is_variadic = true; + } + + // Copy constructor with clone + BareFunctionType (BareFunctionType const &other) + : for_lifetimes (other.for_lifetimes), + function_qualifiers (other.function_qualifiers), params (other.params), + is_variadic (other.is_variadic), variadic_attrs (other.variadic_attrs), + locus (other.locus) + { + // guard to prevent null dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type_no_bounds (); + } + + // Overload assignment operator to deep copy + BareFunctionType &operator= (BareFunctionType const &other) + { + for_lifetimes = other.for_lifetimes; + function_qualifiers = other.function_qualifiers; + params = other.params; + is_variadic = other.is_variadic; + variadic_attrs = other.variadic_attrs; + locus = other.locus; + + // guard to prevent null dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type_no_bounds (); + else + return_type = nullptr; + + return *this; + } + + // move constructors + BareFunctionType (BareFunctionType &&other) = default; + BareFunctionType &operator= (BareFunctionType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems kinda dodgy + std::vector &get_function_params () { return params; } + const std::vector &get_function_params () const + { + return params; + } + + // TODO: would a "vis_type" be better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + + FunctionQualifiers get_function_qualifiers () { return function_qualifiers; } + +protected: + /* Use covariance to implement clone function as returning this object rather + * than base */ + BareFunctionType *clone_type_no_bounds_impl () const override + { + return new BareFunctionType (*this); + } +}; + +// Forward decl - defined in rust-macro.h +class MacroInvocation; + +/* TODO: possible types + * struct type? + * "enum" (tagged union) type? + * C-like union type? + * function item type? + * closure expression types? + * primitive types (bool, int, float, char, str (the slice)) + * Although supposedly TypePaths are used to reference these types (including + * primitives) */ + +/* FIXME: Incomplete spec references: + * anonymous type parameters, aka "impl Trait in argument position" - impl then + * trait bounds abstract return types, aka "impl Trait in return position" - + * impl then trait bounds */ +} // namespace AST +} // namespace Rust + +#endif diff --git a/gcc/rust/operator.h b/gcc/rust/operator.h new file mode 100644 index 00000000000..6813db3ed13 --- /dev/null +++ b/gcc/rust/operator.h @@ -0,0 +1,72 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_OPERATOR_H +#define RUST_OPERATOR_H + +enum class NegationOperator +{ + NEGATE, + NOT +}; + +enum class ArithmeticOrLogicalOperator +{ + ADD, // std::ops::Add + SUBTRACT, // std::ops::Sub + MULTIPLY, // std::ops::Mul + DIVIDE, // std::ops::Div + MODULUS, // std::ops::Rem + BITWISE_AND, // std::ops::BitAnd + BITWISE_OR, // std::ops::BitOr + BITWISE_XOR, // std::ops::BitXor + LEFT_SHIFT, // std::ops::Shl + RIGHT_SHIFT // std::ops::Shr +}; + +enum class ComparisonOperator +{ + EQUAL, // std::cmp::PartialEq::eq + NOT_EQUAL, // std::cmp::PartialEq::ne + GREATER_THAN, // std::cmp::PartialEq::gt + LESS_THAN, // std::cmp::PartialEq::lt + GREATER_OR_EQUAL, // std::cmp::PartialEq::ge + LESS_OR_EQUAL // std::cmp::PartialEq::le +}; + +enum class LazyBooleanOperator +{ + LOGICAL_OR, + LOGICAL_AND +}; + +enum class CompoundAssignmentOperator +{ + ADD, // std::ops::AddAssign + SUBTRACT, // std::ops::SubAssign + MULTIPLY, // std::ops::MulAssign + DIVIDE, // std::ops::DivAssign + MODULUS, // std::ops::RemAssign + BITWISE_AND, // std::ops::BitAndAssign + BITWISE_OR, // std::ops::BitOrAssign + BITWISE_XOR, // std::ops::BitXorAssign + LEFT_SHIFT, // std::ops::ShlAssign + RIGHT_SHIFT // std::ops::ShrAssign +}; + +#endif // RUST_OPERATOR_H -- 2.25.1