diff options
author | SimplyTheOther <simplytheother@gmail.com> | 2020-12-09 15:29:51 +0800 |
---|---|---|
committer | SimplyTheOther <simplytheother@gmail.com> | 2020-12-09 15:29:51 +0800 |
commit | 41c14b45ac604ebd69a6715445cdb10fc5c5ec07 (patch) | |
tree | fb9e6c7e60b9024b1857c0fcf6241c118f2f67f4 /gcc | |
parent | 815c9e8b0734d45a6e5b5a7d50f38d4af7120a8c (diff) | |
parent | cef34bd730d80b4664d8633e2cc27a64c5cae246 (diff) | |
download | gcc-41c14b45ac604ebd69a6715445cdb10fc5c5ec07.zip gcc-41c14b45ac604ebd69a6715445cdb10fc5c5ec07.tar.gz gcc-41c14b45ac604ebd69a6715445cdb10fc5c5ec07.tar.bz2 |
Merge branch 'master' of https://github.com/redbrain/gccrs
Diffstat (limited to 'gcc')
24 files changed, 1559 insertions, 92 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 35d5758..234d51b 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -1,6 +1,6 @@ # Make-lang.in -- Top level -*- makefile -*- fragment for GCC Rust frontend. -# Copyright (C) 2009-2013 Free Software Foundation, Inc. +# Copyright (C) 2009-2020 Free Software Foundation, Inc. # This file is part of GCC. @@ -11,7 +11,7 @@ # 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 +# 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 @@ -30,7 +30,7 @@ GCCRS_INSTALL_NAME := $(shell echo gccrs|sed '$(program_transform_name)') GCCRS_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gccrs|sed '$(program_transform_name)') # Define the names for selecting rust in LANGUAGES. -rust: gccrs$(exeext) rust1$(exeext) +rust: rust1$(exeext) # Tell GNU make to ignore files by these names if they exist. .PHONY: rust @@ -54,7 +54,7 @@ gccrs$(exeext): $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS) $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \ $(EXTRA_GCC_LIBS) $(LIBS) -# List of host object files used by the rust language - files for translation from the parse tree +# List of host object files used by the rust language - files for translation from the parse tree # to GENERIC # The compiler proper, not driver GRS_OBJS = \ @@ -70,12 +70,12 @@ GRS_OBJS = \ rust/rust-ast-full-test.o \ rust/rust-session-manager.o \ rust/rust-name-resolution.o \ - rust/rust-type-resolution.o \ + rust/rust-type-resolution.o \ rust/rust-scan.o \ rust/rust-compile.o \ rust/rust-macro-expand.o \ $(END) -# removed object files from here +# removed object files from here # All language-specific object files for Rust. RUST_ALL_OBJS = $(GRS_OBJS) $(RUST_TARGET_OBJS) @@ -89,11 +89,15 @@ rust1$(exeext): $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBDEPS) # Build hooks. +lang_checks += check-rust +lang_checks_parallelized += check-rust +check_rust_parallelize = 10 + # Copies its dependencies into the source directory. This generally should be used for generated files -# such as Bison output files which are not version-controlled, but should be included in any release -# tarballs. This target will be executed during a bootstrap if ‘--enable-generated-files-in-srcdir’ -# was specified as a configure option. -rust.srcextra: +# such as Bison output files which are not version-controlled, but should be included in any release +# tarballs. This target will be executed during a bootstrap if ‘--enable-generated-files-in-srcdir’ +# was specified as a configure option. +rust.srcextra: rust.all.cross: @@ -101,14 +105,14 @@ rust.all.cross: rust.start.encap: gccrs$(exeext) rust.rest.encap: -# Build generated man pages for the front end from Texinfo manuals (see Man Page Generation), in the -# build directory. This target is only called if the necessary tools are available, but should ignore -# errors so as not to stop the build if errors occur; man pages are optional and the tools involved -# may be installed in a broken way. +# Build generated man pages for the front end from Texinfo manuals (see Man Page Generation), in the +# build directory. This target is only called if the necessary tools are available, but should ignore +# errors so as not to stop the build if errors occur; man pages are optional and the tools involved +# may be installed in a broken way. rust.man: # Copies its dependencies into the source directory. These targets will be executed during a bootstrap -# if ‘--enable-generated-files-in-srcdir’ was specified as a configure option. +# if ‘--enable-generated-files-in-srcdir’ was specified as a configure option. rust.srcman: # Clean hooks. @@ -119,7 +123,7 @@ rust.mostlyclean: rust.clean: rust.mostlyclean # Builds an etags TAGS file in the language subdirectory in the source tree. -# TODO: add more directories if I add more +# TODO: add more directories if I add more rust.tags: force cd $(srcdir)/rust; \ etags -o TAGS.sub *.y *.l *.cc *.h ast/*.h ast/*.cc lex/*.h lex/*.cc parse/*.h parse/*.cc; \ @@ -127,32 +131,32 @@ rust.tags: force # Build documentation hooks. -# Build info documentation for the front end, in the build directory. This target is only called by -# ‘make bootstrap’ if a suitable version of makeinfo is available, so does not need to check for this, -# and should fail if an error occurs. -rust.info: +# Build info documentation for the front end, in the build directory. This target is only called by +# ‘make bootstrap’ if a suitable version of makeinfo is available, so does not need to check for this, +# and should fail if an error occurs. +rust.info: -rust.srcinfo: +rust.srcinfo: -# Build DVI documentation for the front end, in the build directory. This should be done using -# $(TEXI2DVI), with appropriate -I arguments pointing to directories of included files. -rust.dvi: +# Build DVI documentation for the front end, in the build directory. This should be done using +# $(TEXI2DVI), with appropriate -I arguments pointing to directories of included files. +rust.dvi: -# Build PDF documentation for the front end, in the build directory. This should be done using -# $(TEXI2PDF), with appropriate -I arguments pointing to directories of included files. -rust.pdf: +# Build PDF documentation for the front end, in the build directory. This should be done using +# $(TEXI2PDF), with appropriate -I arguments pointing to directories of included files. +rust.pdf: -doc/rust.info: -doc/rust.dvi: -doc/rust.pdf: +doc/rust.info: +doc/rust.dvi: +doc/rust.pdf: -# Build HTML documentation for the front end, in the build directory. -rust.html: +# Build HTML documentation for the front end, in the build directory. +rust.html: # Install hooks. -# Install everything that is part of the front end, apart from the compiler executables listed in -# compilers in config-lang.in. +# Install everything that is part of the front end, apart from the compiler executables listed in +# compilers in config-lang.in. rust.install-common: installdirs # -rm -f $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext) # -rm -f $(DESTDIR)$(bindir)/$(GCCRS_TARGET_INSTALL_NAME)$(exeext) @@ -169,11 +173,11 @@ rust.install-common: installdirs ( cd $(DESTDIR)$(bindir) && \ $(LN) $(GCCRS_INSTALL_NAME)$(exeext) $(GCCRS_TARGET_INSTALL_NAME)$(exeext) ); \ -# Install headers needed for plugins. +# Install headers needed for plugins. rust.install-plugin: -# Uninstall files installed by installing the compiler. This is currently documented not to be -# supported, so the hook need not do anything. +# Uninstall files installed by installing the compiler. This is currently documented not to be +# supported, so the hook need not do anything. rust.uninstall: # -rm -rf $(DESTDIR)/$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext) -rm -f gccrs$(exeext) grs1$(exeext) @@ -183,13 +187,13 @@ rust.uninstall: # No rust-specific selftests selftest-rust: -# Install info documentation for the front end, if it is present in the source directory. This target -# should have dependencies on info files that should be installed. -rust.install-info: +# Install info documentation for the front end, if it is present in the source directory. This target +# should have dependencies on info files that should be installed. +rust.install-info: -rust.install-pdf: +rust.install-pdf: -# Install man pages for the front end. This target should ignore errors. +# Install man pages for the front end. This target should ignore errors. rust.install-man: # Stage hooks: diff --git a/gcc/rust/analysis/rust-type-resolution.cc b/gcc/rust/analysis/rust-type-resolution.cc index 3e2db7f..f1edec8 100644 --- a/gcc/rust/analysis/rust-type-resolution.cc +++ b/gcc/rust/analysis/rust-type-resolution.cc @@ -1,5 +1,6 @@ #include "rust-type-resolution.h" #include "rust-diagnostics.h" +#include "rust-type-visitor.h" #define ADD_BUILTIN_TYPE(_X, _S) \ do \ @@ -104,7 +105,13 @@ TypeResolution::typesAreCompatible (AST::Type *lhs, AST::Type *rhs, } AST::Type *val = NULL; - return scope.LookupType (lhsTypeStr, &val); + if (!scope.LookupType (lhsTypeStr, &val)) + { + rust_error_at (locus, "Unknown type: %s", lhsTypeStr.c_str ()); + return false; + } + + return true; } bool @@ -395,19 +402,133 @@ TypeResolution::visit (AST::CompoundAssignmentExpr &expr) void TypeResolution::visit (AST::GroupedExpr &expr) {} -// void TypeResolution::visit(ArrayElems& elems) {} + void TypeResolution::visit (AST::ArrayElemsValues &elems) -{} +{ + // we need to generate the AST::ArrayType for this array init_expression + // we can get the size via get_num_values() but we need to ensure each element + // are type compatible + + bool failed = false; + AST::Type *last_inferred_type = nullptr; + elems.iterate ([&] (AST::Expr *expr) mutable -> bool { + size_t before; + before = typeBuffer.size (); + expr->accept_vis (*this); + if (typeBuffer.size () <= before) + { + rust_error_at (expr->get_locus_slow (), + "unable to determine element type"); + return false; + } + + AST::Type *inferedType = typeBuffer.back (); + typeBuffer.pop_back (); + + if (last_inferred_type == nullptr) + last_inferred_type = inferedType; + else + { + if (!typesAreCompatible (last_inferred_type, inferedType, + expr->get_locus_slow ())) + { + failed = true; + return false; + } + } + + return true; + }); + + // nothing to do when its failed + if (failed) + return; + + // FIXME This will leak + auto capacity + = new AST::LiteralExpr (std::to_string (elems.get_num_values ()), + AST::Literal::INT, + Linemap::predeclared_location ()); + auto arrayType = new AST::ArrayType (last_inferred_type->clone_type (), + std::unique_ptr<AST::Expr> (capacity), + Linemap::predeclared_location ()); + typeBuffer.push_back (arrayType); +} + void TypeResolution::visit (AST::ArrayElemsCopied &elems) -{} +{ + printf ("ArrayElemsCopied: %s\n", elems.as_string ().c_str ()); +} + void TypeResolution::visit (AST::ArrayExpr &expr) -{} +{ + auto& elements = expr.get_array_elems (); + + auto before = typeBuffer.size (); + elements->accept_vis (*this); + if (typeBuffer.size () <= before) + { + rust_error_at (expr.get_locus_slow (), + "unable to determine type for ArrayExpr"); + return; + } + + expr.set_inferred_type (typeBuffer.back ()); +} + void TypeResolution::visit (AST::ArrayIndexExpr &expr) -{} +{ + auto before = typeBuffer.size (); + expr.get_array_expr ()->accept_vis (*this); + if (typeBuffer.size () <= before) + { + rust_error_at (expr.get_locus_slow (), + "unable to determine type for array index expression"); + return; + } + AST::Type *array_expr_type = typeBuffer.back (); + typeBuffer.pop_back (); + + before = typeBuffer.size (); + expr.get_index_expr ()->accept_vis (*this); + if (typeBuffer.size () <= before) + { + rust_error_at (expr.get_index_expr ()->get_locus_slow (), + "unable to determine type for index expression"); + return; + } + + AST::Type *array_index_type = typeBuffer.back (); + typeBuffer.pop_back (); + + // check the index_type should be an i32 which should really be + // more permissive + AST::Type *i32 = nullptr; + scope.LookupType ("i32", &i32); + rust_assert (i32 != nullptr); + + if (!typesAreCompatible (array_index_type, i32, + expr.get_index_expr ()->get_locus_slow ())) + { + return; + } + + // the the element type from the array_expr_type and it _must_ be an array + AST::ArrayType *resolved = ArrayTypeVisitor::Resolve (array_expr_type); + if (resolved == nullptr) + { + rust_error_at (expr.get_locus_slow (), + "unable to resolve type for array expression"); + return; + } + + typeBuffer.push_back (resolved->get_elem_type ().get ()); +} + void TypeResolution::visit (AST::TupleExpr &expr) {} @@ -516,6 +637,10 @@ TypeResolution::visit (AST::StructExprStructFields &expr) } } + // need to correct the ordering with the respect to the struct definition and + // ensure we handle missing values and give them defaults + // FIXME + // setup a path in type AST::PathIdentSegment seg (expr.get_struct_name ().as_string ()); auto typePath = ::std::unique_ptr<AST::TypePathSegment> ( @@ -649,9 +774,42 @@ TypeResolution::visit (AST::RangeFromToInclExpr &expr) void TypeResolution::visit (AST::RangeToInclExpr &expr) {} + void TypeResolution::visit (AST::ReturnExpr &expr) -{} +{ + // Ensure the type of this matches the function + auto before = typeBuffer.size (); + expr.get_returned_expr ()->accept_vis (*this); + + if (typeBuffer.size () <= before) + { + rust_error_at (expr.get_returned_expr ()->get_locus_slow (), + "unable to determine type for return expr"); + return; + } + + auto inferedType = typeBuffer.back (); + typeBuffer.pop_back (); + + // check this is compatible with the return type + // this will again have issues with structs before we move to HIR + + auto function = scope.CurrentFunction (); + if (!function->has_return_type ()) + { + rust_error_at (expr.get_locus (), "return for void function %s", + function->as_string ().c_str ()); + return; + } + + if (!typesAreCompatible (function->get_return_type ().get (), inferedType, + expr.get_locus_slow ())) + { + return; + } +} + void TypeResolution::visit (AST::UnsafeBlockExpr &expr) {} @@ -764,18 +922,26 @@ TypeResolution::visit (AST::Function &function) // its a marker for a void function scope.InsertType (function.get_function_name (), function.get_return_type ().get ()); scope.InsertFunction (function.get_function_name (), &function); + scope.PushFunction (&function); scope.Push (); for (auto ¶m : function.get_function_params ()) { if (!isTypeInScope (param.get_type ().get (), param.get_locus ())) - return; + { + scope.Pop (); + scope.PopFunction (); + return; + } auto before = letPatternBuffer.size (); param.get_pattern ()->accept_vis (*this); if (letPatternBuffer.size () <= before) { rust_error_at (param.get_locus (), "failed to analyse parameter name"); + + scope.Pop (); + scope.PopFunction (); return; } @@ -788,7 +954,11 @@ TypeResolution::visit (AST::Function &function) if (function.has_return_type ()) { if (!isTypeInScope (function.get_return_type ().get (), function.get_locus ())) - return; + { + scope.Pop (); + scope.PopFunction (); + return; + } } // walk the expression body @@ -802,6 +972,7 @@ TypeResolution::visit (AST::Function &function) function.locals.push_back (value); scope.Pop (); + scope.PopFunction (); } void @@ -1011,7 +1182,7 @@ TypeResolution::visit (AST::LetStmt &stmt) return; } - AST::Type *inferedType = NULL; + AST::Type *inferedType = nullptr; if (stmt.has_init_expr ()) { auto before = typeBuffer.size (); @@ -1044,13 +1215,51 @@ TypeResolution::visit (AST::LetStmt &stmt) return; } } - else if (stmt.has_type () && !stmt.has_init_expr ()) + else if (stmt.has_type ()) { - inferedType = stmt.get_type ().get (); + auto before = typeComparisonBuffer.size (); + stmt.get_type ()->accept_vis (*this); + if (typeComparisonBuffer.size () <= before) + { + rust_error_at (stmt.get_locus (), "failed to understand type for lhs"); + return; + } + auto typeString = typeComparisonBuffer.back (); + typeComparisonBuffer.pop_back (); + + // AST::Type *val = NULL; + // if (!scope.LookupType (typeString, &val)) + // { + // rust_error_at (stmt.locus, "LetStmt has unknown type: %s", + // stmt.type->as_string ().c_str ()); + // return; + // } + } + else if (inferedType != nullptr) + { + auto before = typeComparisonBuffer.size (); + inferedType->accept_vis (*this); + if (typeComparisonBuffer.size () <= before) + { + rust_error_at (stmt.get_locus (), "failed to understand type for lhs"); + return; + } + auto typeString = typeComparisonBuffer.back (); + typeComparisonBuffer.pop_back (); + + // AST::Type *val = NULL; + // if (!scope.LookupType (typeString, &val)) + // { + // rust_error_at (stmt.get_locus (), "Inferred unknown type: %s", + // inferedType->as_string ().c_str ()); + // return; + // } + } + else + { + rust_fatal_error (stmt.get_locus (), "Failed to determine any type for LetStmt"); + return; } - - // TODO check we know what the type is in the scope requires the builtins to - // be defined at the constructor // ensure the decl has the type set for compilation later on if (!stmt.has_type ()) @@ -1118,9 +1327,13 @@ TypeResolution::visit (AST::RawPointerType &type) void TypeResolution::visit (AST::ReferenceType &type) {} + void TypeResolution::visit (AST::ArrayType &type) -{} +{ + typeComparisonBuffer.push_back (type.get_elem_type ()->as_string ()); +} + void TypeResolution::visit (AST::SliceType &type) {} diff --git a/gcc/rust/analysis/rust-type-resolution.h b/gcc/rust/analysis/rust-type-resolution.h index 0c6413b..2f61a39 100644 --- a/gcc/rust/analysis/rust-type-resolution.h +++ b/gcc/rust/analysis/rust-type-resolution.h @@ -1,3 +1,20 @@ +// Copyright (C) 2020 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 +// <http://www.gnu.org/licenses/>. #pragma once #include "rust-resolution.h" @@ -38,6 +55,12 @@ public: return functionScope.Lookup (ident, fn); } + void PushFunction (AST::Function *fn) { functionStack.push_back (fn); } + + void PopFunction () { functionStack.pop_back (); } + + AST::Function *CurrentFunction () { return functionStack.back (); } + void InsertLocal (std::string ident, AST::LetStmt *let) { localsPerBlock.Insert (ident, let); @@ -74,6 +97,8 @@ public: } private: + std::vector<AST::Function *> functionStack; + Scope<AST::Function *> functionScope; Scope<AST::LetStmt *> localsPerBlock; Scope<AST::StructStruct *> structsPerBlock; diff --git a/gcc/rust/analysis/rust-type-visitor.h b/gcc/rust/analysis/rust-type-visitor.h new file mode 100644 index 0000000..8cfe156 --- /dev/null +++ b/gcc/rust/analysis/rust-type-visitor.h @@ -0,0 +1,261 @@ +// rust-type-visitor.h -- Rust AST Visitor to AST::Type specific +// Copyright (C) 2020 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 +// <http://www.gnu.org/licenses/>. + +#ifndef RUST_TYPE_VISITOR_H +#define RUST_TYPE_VISITOR_H + +#include "rust-system.h" +#include "rust-ast-full.h" +#include "rust-ast-visitor.h" +#include "rust-scan.h" + +namespace Rust { +namespace Analysis { + +class BaseTypeVisitor : public AST::ASTVisitor +{ +public: + // visitor impl + // rust-ast.h + // virtual void visit(AttrInput& attr_input); + // virtual void visit(TokenTree& token_tree); + // virtual void visit(MacroMatch& macro_match); + virtual void visit (AST::Token &tok) override {} + virtual void visit (AST::DelimTokenTree &delim_tok_tree) override {} + virtual void visit (AST::AttrInputMetaItemContainer &input) override {} + // virtual void visit(MetaItem& meta_item) override {} + // void vsit(Stmt& stmt) override {} + // virtual void visit(Expr& expr) override {} + virtual void visit (AST::IdentifierExpr &ident_expr) override {} + // virtual void visit(Pattern& pattern) override {} + // virtual void visit(Type& type) override {} + // virtual void visit(TypeParamBound& type_param_bound) override {} + virtual void visit (AST::Lifetime &lifetime) override {} + // virtual void visit(GenericParam& generic_param) override {} + virtual void visit (AST::LifetimeParam &lifetime_param) override {} + // virtual void visit(TraitItem& trait_item) override {} + // virtual void visit(InherentImplItem& inherent_impl_item) override {} + // virtual void visit(TraitImplItem& trait_impl_item) override {} + virtual void visit (AST::MacroInvocationSemi ¯o) override {} + + // rust-path.h + virtual void visit (AST::PathInExpression &path) override {} + virtual void visit (AST::TypePathSegment &segment) override {} + virtual void visit (AST::TypePathSegmentGeneric &segment) override {} + virtual void visit (AST::TypePathSegmentFunction &segment) override {} + virtual void visit (AST::TypePath &path) override {} + virtual void visit (AST::QualifiedPathInExpression &path) override {} + virtual void visit (AST::QualifiedPathInType &path) override {} + + // rust-expr.h + virtual void visit (AST::LiteralExpr &expr) override {} + virtual void visit (AST::AttrInputLiteral &attr_input) override {} + virtual void visit (AST::MetaItemLitExpr &meta_item) override {} + virtual void visit (AST::MetaItemPathLit &meta_item) override {} + virtual void visit (AST::BorrowExpr &expr) override {} + virtual void visit (AST::DereferenceExpr &expr) override {} + virtual void visit (AST::ErrorPropagationExpr &expr) override {} + virtual void visit (AST::NegationExpr &expr) override {} + virtual void visit (AST::ArithmeticOrLogicalExpr &expr) override {} + virtual void visit (AST::ComparisonExpr &expr) override {} + virtual void visit (AST::LazyBooleanExpr &expr) override {} + virtual void visit (AST::TypeCastExpr &expr) override {} + virtual void visit (AST::AssignmentExpr &expr) override {} + virtual void visit (AST::CompoundAssignmentExpr &expr) override {} + virtual void visit (AST::GroupedExpr &expr) override {} + // virtual void visit(ArrayElems& elems) override {} + virtual void visit (AST::ArrayElemsValues &elems) override {} + virtual void visit (AST::ArrayElemsCopied &elems) override {} + virtual void visit (AST::ArrayExpr &expr) override {} + virtual void visit (AST::ArrayIndexExpr &expr) override {} + virtual void visit (AST::TupleExpr &expr) override {} + virtual void visit (AST::TupleIndexExpr &expr) override {} + virtual void visit (AST::StructExprStruct &expr) override {} + // virtual void visit(StructExprField& field) override {} + virtual void visit (AST::StructExprFieldIdentifier &field) override {} + virtual void visit (AST::StructExprFieldIdentifierValue &field) override {} + virtual void visit (AST::StructExprFieldIndexValue &field) override {} + virtual void visit (AST::StructExprStructFields &expr) override {} + virtual void visit (AST::StructExprStructBase &expr) override {} + virtual void visit (AST::StructExprTuple &expr) override {} + virtual void visit (AST::StructExprUnit &expr) override {} + // virtual void visit(EnumExprField& field) override {} + virtual void visit (AST::EnumExprFieldIdentifier &field) override {} + virtual void visit (AST::EnumExprFieldIdentifierValue &field) override {} + virtual void visit (AST::EnumExprFieldIndexValue &field) override {} + virtual void visit (AST::EnumExprStruct &expr) override {} + virtual void visit (AST::EnumExprTuple &expr) override {} + virtual void visit (AST::EnumExprFieldless &expr) override {} + virtual void visit (AST::CallExpr &expr) override {} + virtual void visit (AST::MethodCallExpr &expr) override {} + virtual void visit (AST::FieldAccessExpr &expr) override {} + virtual void visit (AST::ClosureExprInner &expr) override {} + virtual void visit (AST::BlockExpr &expr) override {} + virtual void visit (AST::ClosureExprInnerTyped &expr) override {} + virtual void visit (AST::ContinueExpr &expr) override {} + virtual void visit (AST::BreakExpr &expr) override {} + virtual void visit (AST::RangeFromToExpr &expr) override {} + virtual void visit (AST::RangeFromExpr &expr) override {} + virtual void visit (AST::RangeToExpr &expr) override {} + virtual void visit (AST::RangeFullExpr &expr) override {} + virtual void visit (AST::RangeFromToInclExpr &expr) override {} + virtual void visit (AST::RangeToInclExpr &expr) override {} + virtual void visit (AST::ReturnExpr &expr) override {} + virtual void visit (AST::UnsafeBlockExpr &expr) override {} + virtual void visit (AST::LoopExpr &expr) override {} + virtual void visit (AST::WhileLoopExpr &expr) override {} + virtual void visit (AST::WhileLetLoopExpr &expr) override {} + virtual void visit (AST::ForLoopExpr &expr) override {} + virtual void visit (AST::IfExpr &expr) override {} + virtual void visit (AST::IfExprConseqElse &expr) override {} + virtual void visit (AST::IfExprConseqIf &expr) override {} + virtual void visit (AST::IfExprConseqIfLet &expr) override {} + virtual void visit (AST::IfLetExpr &expr) override {} + virtual void visit (AST::IfLetExprConseqElse &expr) override {} + virtual void visit (AST::IfLetExprConseqIf &expr) override {} + virtual void visit (AST::IfLetExprConseqIfLet &expr) override {} + // virtual void visit(MatchCase& match_case) override {} + // virtual void visit (AST::MatchCaseBlockExpr &match_case) override {} + // virtual void visit (AST::MatchCaseExpr &match_case) override {} + virtual void visit (AST::MatchExpr &expr) override {} + virtual void visit (AST::AwaitExpr &expr) override {} + virtual void visit (AST::AsyncBlockExpr &expr) override {} + + // rust-item.h + virtual void visit (AST::TypeParam ¶m) override {} + // virtual void visit(WhereClauseItem& item) override {} + virtual void visit (AST::LifetimeWhereClauseItem &item) override {} + virtual void visit (AST::TypeBoundWhereClauseItem &item) override {} + virtual void visit (AST::Method &method) override {} + virtual void visit (AST::ModuleBodied &module) override {} + virtual void visit (AST::ModuleNoBody &module) override {} + virtual void visit (AST::ExternCrate &crate) override {} + // virtual void visit(UseTree& use_tree) override {} + virtual void visit (AST::UseTreeGlob &use_tree) override {} + virtual void visit (AST::UseTreeList &use_tree) override {} + virtual void visit (AST::UseTreeRebind &use_tree) override {} + virtual void visit (AST::UseDeclaration &use_decl) override {} + virtual void visit (AST::Function &function) override {} + virtual void visit (AST::TypeAlias &type_alias) override {} + virtual void visit (AST::StructStruct &struct_item) override {} + virtual void visit (AST::TupleStruct &tuple_struct) override {} + virtual void visit (AST::EnumItem &item) override {} + virtual void visit (AST::EnumItemTuple &item) override {} + virtual void visit (AST::EnumItemStruct &item) override {} + virtual void visit (AST::EnumItemDiscriminant &item) override {} + virtual void visit (AST::Enum &enum_item) override {} + virtual void visit (AST::Union &union_item) override {} + virtual void visit (AST::ConstantItem &const_item) override {} + virtual void visit (AST::StaticItem &static_item) override {} + virtual void visit (AST::TraitItemFunc &item) override {} + virtual void visit (AST::TraitItemMethod &item) override {} + virtual void visit (AST::TraitItemConst &item) override {} + virtual void visit (AST::TraitItemType &item) override {} + virtual void visit (AST::Trait &trait) override {} + virtual void visit (AST::InherentImpl &impl) override {} + virtual void visit (AST::TraitImpl &impl) override {} + // virtual void visit(ExternalItem& item) override {} + virtual void visit (AST::ExternalStaticItem &item) override {} + virtual void visit (AST::ExternalFunctionItem &item) override {} + virtual void visit (AST::ExternBlock &block) override {} + + // rust-macro.h + virtual void visit (AST::MacroMatchFragment &match) override {} + virtual void visit (AST::MacroMatchRepetition &match) override {} + virtual void visit (AST::MacroMatcher &matcher) override {} + virtual void visit (AST::MacroRulesDefinition &rules_def) override {} + virtual void visit (AST::MacroInvocation ¯o_invoc) override {} + virtual void visit (AST::MetaItemPath &meta_item) override {} + virtual void visit (AST::MetaItemSeq &meta_item) override {} + virtual void visit (AST::MetaWord &meta_item) override {} + virtual void visit (AST::MetaNameValueStr &meta_item) override {} + virtual void visit (AST::MetaListPaths &meta_item) override {} + virtual void visit (AST::MetaListNameValueStr &meta_item) override {} + + // rust-pattern.h + virtual void visit (AST::LiteralPattern &pattern) override {} + virtual void visit (AST::IdentifierPattern &pattern) override {} + virtual void visit (AST::WildcardPattern &pattern) override {} + // virtual void visit(RangePatternBound& bound) override {} + virtual void visit (AST::RangePatternBoundLiteral &bound) override {} + virtual void visit (AST::RangePatternBoundPath &bound) override {} + virtual void visit (AST::RangePatternBoundQualPath &bound) override {} + virtual void visit (AST::RangePattern &pattern) override {} + virtual void visit (AST::ReferencePattern &pattern) override {} + // virtual void visit(StructPatternField& field) override {} + virtual void visit (AST::StructPatternFieldTuplePat &field) override {} + virtual void visit (AST::StructPatternFieldIdentPat &field) override {} + virtual void visit (AST::StructPatternFieldIdent &field) override {} + virtual void visit (AST::StructPattern &pattern) override {} + // virtual void visit(TupleStructItems& tuple_items) override {} + virtual void visit (AST::TupleStructItemsNoRange &tuple_items) override {} + virtual void visit (AST::TupleStructItemsRange &tuple_items) override {} + virtual void visit (AST::TupleStructPattern &pattern) override {} + // virtual void visit(TuplePatternItems& tuple_items) override {} + virtual void visit (AST::TuplePatternItemsMultiple &tuple_items) override {} + virtual void visit (AST::TuplePatternItemsRanged &tuple_items) override {} + virtual void visit (AST::TuplePattern &pattern) override {} + virtual void visit (AST::GroupedPattern &pattern) override {} + virtual void visit (AST::SlicePattern &pattern) override {} + + // rust-stmt.h + virtual void visit (AST::EmptyStmt &stmt) override {} + virtual void visit (AST::LetStmt &stmt) override {} + virtual void visit (AST::ExprStmtWithoutBlock &stmt) override {} + virtual void visit (AST::ExprStmtWithBlock &stmt) override {} + + // rust-type.h + virtual void visit (AST::TraitBound &bound) override {} + virtual void visit (AST::ImplTraitType &type) override {} + virtual void visit (AST::TraitObjectType &type) override {} + virtual void visit (AST::ParenthesisedType &type) override {} + virtual void visit (AST::ImplTraitTypeOneBound &type) override {} + virtual void visit (AST::TraitObjectTypeOneBound &type) override {} + virtual void visit (AST::TupleType &type) override {} + virtual void visit (AST::NeverType &type) override {} + virtual void visit (AST::RawPointerType &type) override {} + virtual void visit (AST::ReferenceType &type) override {} + virtual void visit (AST::ArrayType &type) override {} + virtual void visit (AST::SliceType &type) override {} + virtual void visit (AST::InferredType &type) override {} + virtual void visit (AST::BareFunctionType &type) override {} +}; + +class ArrayTypeVisitor : public BaseTypeVisitor +{ +public: + static AST::ArrayType *Resolve (AST::Type *type) + { + ArrayTypeVisitor vis; + type->accept_vis (vis); + return vis.resolved; + } + + virtual void visit (AST::ArrayType &type) override { resolved = &type; }; + +private: + ArrayTypeVisitor () : resolved (nullptr) {} + + AST::ArrayType *resolved; +}; + +} // namespace Analysis +} // namespace Rust + +#endif // RUST_TYPE_VISITOR_H diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h index 34dfc25..c7302fe 100644 --- a/gcc/rust/ast/rust-expr.h +++ b/gcc/rust/ast/rust-expr.h @@ -972,6 +972,17 @@ public: const std::vector<std::unique_ptr<Expr> > &get_values () const { return values; } std::vector<std::unique_ptr<Expr> > &get_values () { return values; } + size_t get_num_values () const { return values.size (); } + + void iterate (std::function<bool (Expr *)> cb) + { + for (auto it = values.begin (); it != values.end (); it++) + { + if (!cb ((*it).get ())) + return; + } + } + protected: ArrayElemsValues *clone_array_elems_impl () const override { @@ -1048,6 +1059,10 @@ class ArrayExpr : public ExprWithoutBlock // TODO: find another way to store this to save memory? bool marked_for_strip = false; + // this is a reference to what the inferred type is based on + // this init expression + Type *inferredType; + public: std::string as_string () const override; @@ -1111,6 +1126,9 @@ public: return internal_elements; } + Type *get_inferred_type () { return inferredType; } + void set_inferred_type (Type *type) { inferredType = type; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ diff --git a/gcc/rust/ast/rust-type.h b/gcc/rust/ast/rust-type.h index ea9a89c..4351017 100644 --- a/gcc/rust/ast/rust-type.h +++ b/gcc/rust/ast/rust-type.h @@ -64,7 +64,7 @@ class ImplTraitType : public Type { // TypeParamBounds type_param_bounds; // inlined form - std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds; + std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds; Location locus; @@ -78,7 +78,7 @@ protected: public: ImplTraitType ( - std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds, + std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds, Location locus) : type_param_bounds (std::move (type_param_bounds)), locus (locus) {} @@ -124,7 +124,7 @@ class TraitObjectType : public Type { bool has_dyn; // TypeParamBounds type_param_bounds; - std::vector<std::unique_ptr<TypeParamBound>> + std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds; // inlined form Location locus; @@ -139,7 +139,7 @@ protected: public: TraitObjectType ( - std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds, + std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds, Location locus, bool is_dyn_dispatch = false) : has_dyn (is_dyn_dispatch), type_param_bounds (std::move (type_param_bounds)), locus (locus) @@ -330,14 +330,14 @@ class TypePath; // definition moved to "rust-path.h" * specific order */ class TupleType : public TypeNoBounds { - std::vector<std::unique_ptr<Type>> elems; + std::vector<std::unique_ptr<Type> > 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<std::unique_ptr<Type>> elems, Location locus) + TupleType (std::vector<std::unique_ptr<Type> > elems, Location locus) : elems (std::move (elems)), locus (locus) {} diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc index 3691f9e..dd28759 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -503,19 +503,102 @@ Compilation::visit (AST::CompoundAssignmentExpr &expr) void Compilation::visit (AST::GroupedExpr &expr) {} -// void Compilation::visit(ArrayElems& elems) {} + void Compilation::visit (AST::ArrayElemsValues &elems) -{} +{ + std::vector< ::Bexpression *> elements; + + bool failed = false; + elems.iterate ([&] (AST::Expr *expr) mutable -> bool { + Bexpression *value = nullptr; + VISIT_POP (expr.get_locus_slow (), expr, value, exprs); + if (value == nullptr) + { + rust_fatal_error (expr->get_locus_slow (), + "failed to compile value to array initialiser"); + return false; + } + elements.push_back (value); + return true; + }); + + // nothing to do when its failed + if (failed) + return; + + arrayConsStack.push_back (elements); +} + void Compilation::visit (AST::ArrayElemsCopied &elems) {} + void Compilation::visit (AST::ArrayExpr &expr) -{} +{ + translatedType = nullptr; + expr.get_inferred_type ()->accept_vis (*this); + if (translatedType == nullptr) + { + rust_error_at (expr.get_locus_slow (), + "failed to compile array type for ArrayExpr"); + return; + } + + ::Btype *compiledType = translatedType; + translatedType = nullptr; + + auto before = arrayConsStack.size (); + expr.get_internal_elements ()->accept_vis (*this); + if (arrayConsStack.size () <= before) + { + rust_error_at (expr.get_locus_slow (), + "failed to compile the array constructor"); + return; + } + std::vector< ::Bexpression *> initializer = arrayConsStack.back (); + arrayConsStack.pop_back (); + + std::vector<unsigned long> indexes; + for (unsigned long i = 0; i < initializer.size (); ++i) + indexes.push_back (i); + + Bexpression *cons + = backend->array_constructor_expression (compiledType, indexes, initializer, + expr.get_locus_slow ()); + exprs.push_back (cons); +} + void Compilation::visit (AST::ArrayIndexExpr &expr) -{} +{ + Bexpression *arrayExpr = nullptr; + VISIT_POP (expr.get_array_expr ()->get_locus_slow (), expr.get_array_expr (), + arrayExpr, exprs); + if (arrayExpr == nullptr) + { + rust_error_at (expr.get_locus_slow (), + "failed to compile value to array expression reference"); + return; + } + + Bexpression *indexExpr = nullptr; + VISIT_POP (expr.get_index_expr ()->get_locus_slow (), expr.get_index_expr (), + indexExpr, exprs); + if (indexExpr == nullptr) + { + rust_error_at (expr.get_locus_slow (), + "failed to compile value to array index expression"); + return; + } + + Bexpression *indexExpression + = backend->array_index_expression (arrayExpr, indexExpr, + expr.get_locus_slow ()); + exprs.push_back (indexExpression); +} + void Compilation::visit (AST::TupleExpr &expr) {} @@ -533,24 +616,6 @@ Compilation::visit (AST::StructExprFieldIdentifier &field) void Compilation::visit (AST::StructExprFieldIdentifierValue &field) { - AST::StructStruct *decl = structBuffer.back (); - size_t index = 0; - bool found = false; - for (auto &df : decl->get_fields ()) - { - if (field.get_field_name ().compare (df.get_field_name ()) == 0) - { - found = true; - break; - } - } - if (!found) - { - rust_fatal_error (field.get_value ()->get_locus_slow (), - "failed to lookup field index"); - return; - } - Bexpression *value = NULL; VISIT_POP (field.get_value ()->get_locus_slow (), field.get_value ().get (), value, exprs); if (value == NULL) @@ -1394,9 +1459,34 @@ Compilation::visit (AST::RawPointerType &type) void Compilation::visit (AST::ReferenceType &type) {} + void Compilation::visit (AST::ArrayType &type) -{} +{ + Btype *elementType; + translatedType = nullptr; + type.get_element_type ()->accept_vis (*this); + if (translatedType == nullptr) + { + rust_error_at (type.get_locus (), + "Failed to compile element type for array"); + return; + } + elementType = translatedType; + + Bexpression *length = nullptr; + VISIT_POP (type.get_size_expr ()->get_locus_slow (), type.get_size_expr (), + length, exprs); + if (length == nullptr) + { + rust_error_at (type.get_size_expr ()->get_locus_slow (), + "failed to size for array type"); + return; + } + + translatedType = backend->array_type (elementType, length); +} + void Compilation::visit (AST::SliceType &type) {} diff --git a/gcc/rust/backend/rust-compile.h b/gcc/rust/backend/rust-compile.h index ba03483..88c8318 100644 --- a/gcc/rust/backend/rust-compile.h +++ b/gcc/rust/backend/rust-compile.h @@ -243,7 +243,8 @@ private: std::vector< ::Bexpression *> exprs; std::vector< ::Bstatement *> stmts; std::vector< ::Bvariable *> varBuffer; - std::vector<AST::StructStruct*> structBuffer; + std::vector<AST::StructStruct *> structBuffer; + std::vector<std::vector< ::Bexpression *> > arrayConsStack; // careful these are the vectors we pass into the GCC middle-end std::vector< ::Btype *> type_decls; diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h index ccc5e47..a907fe4 100644 --- a/gcc/rust/rust-backend.h +++ b/gcc/rust/rust-backend.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef RUST_BACKEND_H +#define RUST_BACKEND_H #include <gmp.h> #include <mpfr.h> @@ -759,3 +760,5 @@ public: // section in the output object file. virtual void write_export_data (const char *bytes, unsigned int size) = 0; }; + +#endif // RUST_BACKEND_H diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index fd50bf1..5da2764 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -279,6 +279,12 @@ Session::init () // derived values from hook options.target_data.init_derived_values (); + + // setup singleton linemap + linemap = rust_get_linemap (); + + // setup backend to GCC GIMPLE + backend = rust_get_backend (); } /* Initialise default options. Actually called before handle_option, unlike init @@ -416,12 +422,10 @@ Session::parse_file (const char *filename) rust_fatal_error (Location (), "cannot open filename %s: %m", filename); } - Backend *backend = rust_get_backend (); - // parse file here /* create lexer and parser - these are file-specific and so aren't instance * variables */ - Lexer lex (filename, std::move (file_wrap), rust_get_linemap ()); + Lexer lex (filename, std::move (file_wrap), linemap); Parser<Lexer> parser (std::move (lex)); // generate crate from parser diff --git a/gcc/testsuite/lib/rust-dg.exp b/gcc/testsuite/lib/rust-dg.exp new file mode 100644 index 0000000..e7c9852 --- /dev/null +++ b/gcc/testsuite/lib/rust-dg.exp @@ -0,0 +1,106 @@ +# Copyright (C) 2012-2019 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# <http://www.gnu.org/licenses/>. + +load_lib gcc-dg.exp + +# Define rust callbacks for dg.exp. + +proc rust-dg-test { prog do_what extra_tool_flags } { + upvar dg-do-what dg-do-what + + # Demote link and run tests to compile-only if D runtime is missing. + if ![check_effective_target_d_runtime] { + switch $do_what { + link - + run { + set do_what compile + set dg-do-what compile + } + } + } + + # Strip rust.test prefix off test names to avoid pathname failures in + # some tests. + set prog [dg-trim-dirname rust.test $prog] + + set result \ + [gcc-dg-test-1 rust_target_compile $prog $do_what $extra_tool_flags] + + set comp_output [lindex $result 0] + set output_file [lindex $result 1] + + return [list $comp_output $output_file] +} + +proc rust-dg-prune { system text } { + return [gcc-dg-prune $system $text] +} + +# Utility routines. + +# +# Modified dg-runtest that can cycle through a list of optimization options +# as c-torture does. +# + +proc rust-dg-runtest { testcases flags default-extra-flags } { + global runtests + + foreach test $testcases { + # If we're only testing specific files and this isn't one of + # them, skip it. + + if ![runtest_file_p $runtests $test] { + continue + } + + # Use TORTURE_OPTIONS to cycle through an option list. + if [torture-options-exist] then { + global torture_with_loops + set option_list $torture_with_loops + } else { + set option_list { "" } + } + + set nshort [file tail [file dirname $test]]/[file tail $test] + + foreach flags_t $option_list { + verbose "Testing $nshort, $flags $flags_t" 1 + dg-test $test "$flags $flags_t" ${default-extra-flags} + } + } +} + +# +# rust_load -- wrapper around default rust_load to handle tests that +# require program arguments passed to them. +# + +if { [info procs rust_load] != [list] \ + && [info procs prev_rust_load] == [list] } { + rename rust_load prev_rust_load + + proc rust_load { program args } { + global RUST_EXECUTE_ARGS + if [info exists RUST_EXECUTE_ARGS] then { + set args [concat "{$RUST_EXECUTE_ARGS}"] + } + #print "Running: $program [lindex $args 0]" + set result [eval [list prev_rust_load $program] $args ] + return $result + } +} + diff --git a/gcc/testsuite/lib/rust.exp b/gcc/testsuite/lib/rust.exp new file mode 100644 index 0000000..541b052 --- /dev/null +++ b/gcc/testsuite/lib/rust.exp @@ -0,0 +1,213 @@ +# Copyright (C) 2012-2020 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# <http://www.gnu.org/licenses/>. + +# +# rust support library routines +# + +load_lib prune.exp +load_lib gcc-defs.exp +load_lib timeout.exp +load_lib target-libpath.exp + +# +# RUST_UNDER_TEST is the compiler under test. +# + +set rust_compile_options "" + + +# +# rust_version -- extract and print the version number of the compiler +# + +proc rust_version { } { + global RUST_UNDER_TEST + + rust_init + + # ignore any arguments after the command + set compiler [lindex $RUST_UNDER_TEST 0] + + # verify that the compiler exists + if { [is_remote host] || [which $compiler] != 0 } then { + set tmp [remote_exec host "$compiler -v"] + set status [lindex $tmp 0] + set output [lindex $tmp 1] + regexp " version \[^\n\r\]*" $output version + if { $status == 0 && [info exists version] } then { + if [is_remote host] { + clone_output "$compiler $version\n" + } else { + clone_output "[which $compiler] $version\n" + } + } else { + clone_output "Couldn't determine version of $output [which $compiler]\n" + } + } else { + # compiler does not exist (this should have already been detected) + warning "$compiler does not exist" + } +} + +# +# rust_include_flags -- include flags for the gcc tree structure +# + +proc rust_include_flags { paths } { + global srcdir + global TESTING_IN_BUILD_TREE + + set flags "" + + if { [is_remote host] || ![info exists TESTING_IN_BUILD_TREE] } { + return "${flags}" + } + + set gccpath ${paths} + + return "$flags" +} + +# +# rust_link_flags -- linker flags for the gcc tree structure +# + +proc rust_link_flags { paths } { + global srcdir + global ld_library_path + global RUST_UNDER_TEST + global shlib_ext + global SHARED_OPTION + + set gccpath ${paths} + set libio_dir "" + set flags "" + set ld_library_path "." + set shlib_ext [get_shlib_extension] + set SHARED_OPTION "" + verbose "shared lib extension: $shlib_ext" + + set_ld_library_path_env_vars + + return "$flags" +} + +# +# rust_init -- called at the start of each subdir of tests +# + +proc rust_init { args } { + global subdir + global rust_initialized + global base_dir + global tmpdir + global libdir + global gluefile wrap_flags + global objdir srcdir + global ALWAYS_DFLAGS + global TOOL_EXECUTABLE TOOL_OPTIONS + global RUST_UNDER_TEST + global TESTING_IN_BUILD_TREE + global TEST_ALWAYS_FLAGS + global gcc_warning_prefix + global gcc_error_prefix + + # We set LC_ALL and LANG to C so that we get the same error messages as expected. + setenv LC_ALL C + setenv LANG C + + if ![info exists RUST_UNDER_TEST] then { + if [info exists TOOL_EXECUTABLE] { + set RUST_UNDER_TEST $TOOL_EXECUTABLE + } else { + if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } { + set RUST_UNDER_TEST [transform gccrs] + } else { + set RUST_UNDER_TEST [findfile $base_dir/../../gccrs "$base_dir/../../gccrs -B$base_dir/../../" [findfile $base_dir/gccrs "$base_dir/gccrs -B$base_dir/" [transform gccrs]]] + } + } + } + + if ![is_remote host] { + if { [which $RUST_UNDER_TEST] == 0 } then { + perror "RUST_UNDER_TEST ($RUST_UNDER_TEST) does not exist" + exit 1 + } + } + if ![info exists tmpdir] { + set tmpdir "/tmp" + } + + if [info exists gluefile] { + unset gluefile + } + + rust_maybe_build_wrapper "${tmpdir}/rust-testglue.o" + + set ALWAYS_RUSTFLAGS "" + + # TEST_ALWAYS_FLAGS are flags that should be passed to every + # compilation. They are passed first to allow individual + # tests to override them. + if [info exists TEST_ALWAYS_FLAGS] { + lappend ALWAYS_DFLAGS "additional_flags=$TEST_ALWAYS_FLAGS" + } + + if ![is_remote host] { + if [info exists TOOL_OPTIONS] { + lappend ALWAYS_DFLAGS "additional_flags=[rust_include_flags [get_multilibs ${TOOL_OPTIONS}] ]" + lappend ALWAYS_DFLAGS "ldflags=[rust_link_flags [get_multilibs ${TOOL_OPTIONS}] ]" + } else { + lappend ALWAYS_DFLAGS "additional_flags=[rust_include_flags [get_multilibs] ]" + lappend ALWAYS_DFLAGS "ldflags=[rust_link_flags [get_multilibs] ]" + } + } + + if [info exists TOOL_OPTIONS] { + lappend ALWAYS_DFLAGS "additional_flags=$TOOL_OPTIONS" + } + + verbose -log "ALWAYS_DFLAGS set to $ALWAYS_DFLAGS" + + set gcc_warning_prefix "warning:" + set gcc_error_prefix "(fatal )?error:" + + verbose "rust is initialized" 3 +} + +# +# rust_target_compile -- compile a source file +# + +proc rust_target_compile { source dest type options } { + global tmpdir + global gluefile wrap_flags + global ALWAYS_DFLAGS + global RUST_UNDER_TEST + + if { [target_info needs_status_wrapper] != "" && [info exists gluefile] } { + lappend options "libs=${gluefile}" + lappend options "ldflags=${wrap_flags}" + } + + lappend options "timeout=[timeout_value]" + lappend options "compiler=$RUST_UNDER_TEST" + + set options [concat "$ALWAYS_DFLAGS" $options] + set options [dg-additional-files-options $options $source] + return [target_compile $source $dest $type $options] +} diff --git a/gcc/testsuite/rust.test/compilable/arrays1.rs b/gcc/testsuite/rust.test/compilable/arrays1.rs new file mode 100644 index 0000000..f7e66af --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/arrays1.rs @@ -0,0 +1,7 @@ +fn main() { + let xs: [i32; 5] = [1, 2, 3, 4, 5]; + let xy = [6, 7, 8]; + + let a = xs[0]; + let b = xy[2]; +} diff --git a/gcc/testsuite/rust.test/compilable/conditional.rs b/gcc/testsuite/rust.test/compilable/conditional.rs new file mode 100644 index 0000000..2bb3a95 --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/conditional.rs @@ -0,0 +1,11 @@ +fn main() { + let mut x = 5; + + if x == 5 { + x = 1; + } else if x == 3 { + x = 2; + } else { + x = 3; + } +} diff --git a/gcc/testsuite/rust.test/compilable/impl_block.rs b/gcc/testsuite/rust.test/compilable/impl_block.rs new file mode 100644 index 0000000..51ed74b --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/impl_block.rs @@ -0,0 +1,14 @@ +struct Foo { + one: i32, + two: i32, +} + +impl Foo { + fn new(a: i32, b: i32) -> Foo { + return Foo { one: a, two: b }; + } +} + +fn main() { + let cake = Foo::new(3, 4); +} diff --git a/gcc/testsuite/rust.test/compilable/static_function.rs b/gcc/testsuite/rust.test/compilable/static_function.rs new file mode 100644 index 0000000..7a564af --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/static_function.rs @@ -0,0 +1,7 @@ +fn test(x: i32) -> i32 { + return x + 1; +} + +fn main() { + let call_test = test(1); +} diff --git a/gcc/testsuite/rust.test/compilable/struct_init.rs b/gcc/testsuite/rust.test/compilable/struct_init.rs new file mode 100644 index 0000000..6081476 --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/struct_init.rs @@ -0,0 +1,8 @@ +struct Foo { + one: i32, + two: i32, +} + +fn main() { + let struct_test = Foo { one: 1, two: 2 }; +} diff --git a/gcc/testsuite/rust.test/compilable/type_infer1.rs b/gcc/testsuite/rust.test/compilable/type_infer1.rs new file mode 100644 index 0000000..bc4b09a --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/type_infer1.rs @@ -0,0 +1,19 @@ +struct Foo { + one: i32, + two: i32, +} + +fn test(x: i32) -> i32 { + return x + 1; +} + +fn main() { + let logical: bool = true; + let an_integer = 5; + let mut default_integer = 7; + + default_integer = 1 + an_integer; + + let call_test = test(1); + let struct_test = Foo { one: 1, two: 2 }; +} diff --git a/gcc/testsuite/rust.test/compilable/type_infer2.rs b/gcc/testsuite/rust.test/compilable/type_infer2.rs new file mode 100644 index 0000000..614c67d --- /dev/null +++ b/gcc/testsuite/rust.test/compilable/type_infer2.rs @@ -0,0 +1,8 @@ +fn test(x: i32) -> i32 { + return x + 1; +} + +fn main() { + let an_integer = 5; + let call_test = test(an_integer + 1); +} diff --git a/gcc/testsuite/rust.test/fail_compilation/arrays1.rs b/gcc/testsuite/rust.test/fail_compilation/arrays1.rs new file mode 100644 index 0000000..797f1ca --- /dev/null +++ b/gcc/testsuite/rust.test/fail_compilation/arrays1.rs @@ -0,0 +1,4 @@ +fn main() { + let xs: [i32; 5] = [1, 2, 3, 4, 5]; + let a: bool = xs[0]; +} diff --git a/gcc/testsuite/rust.test/fail_compilation/arrays2.rs b/gcc/testsuite/rust.test/fail_compilation/arrays2.rs new file mode 100644 index 0000000..9ecf322 --- /dev/null +++ b/gcc/testsuite/rust.test/fail_compilation/arrays2.rs @@ -0,0 +1,3 @@ +fn main() { + let array: [i32; 5] = [1, 2, 3]; +} diff --git a/gcc/testsuite/rust.test/fail_compilation/bad_type1.rs b/gcc/testsuite/rust.test/fail_compilation/bad_type1.rs new file mode 100644 index 0000000..83e16f3 --- /dev/null +++ b/gcc/testsuite/rust.test/fail_compilation/bad_type1.rs @@ -0,0 +1,3 @@ +fn main() { + let logical: bool = 123; +} diff --git a/gcc/testsuite/rust.test/fail_compilation/func1.rs b/gcc/testsuite/rust.test/fail_compilation/func1.rs new file mode 100644 index 0000000..5578c27 --- /dev/null +++ b/gcc/testsuite/rust.test/fail_compilation/func1.rs @@ -0,0 +1,9 @@ +fn test(x: i32) -> bool { + return x + 1; +} + +fn main() { + let an_integer = 5; + + let call_test = test(1); +} diff --git a/gcc/testsuite/rust.test/rust-test.exp b/gcc/testsuite/rust.test/rust-test.exp new file mode 100644 index 0000000..cc32454 --- /dev/null +++ b/gcc/testsuite/rust.test/rust-test.exp @@ -0,0 +1,436 @@ +# Copyright (C) 2012-2019 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# <http://www.gnu.org/licenses/>. + +# Test using the DMD testsuite. +# Load support procs. +load_lib rust-dg.exp + +# +# Convert DMD arguments to RUST equivalent +# + +proc rust-convert-args { args } { + set out "" + + foreach arg [split [lindex $args 0] " "] { + # List of switches kept in ASCII collated order. + if [string match "-D" $arg] { + lappend out "-fdoc" + + } elseif { [regexp -- {^-I([\w+/-]+)} $arg pattern path] } { + lappend out "-I$path" + + } elseif { [regexp -- {^-J([\w+/-]+)} $arg pattern path] } { + lappend out "-J$path" + + } elseif [string match "-allinst" $arg] { + lappend out "-fall-instantiations" + + } elseif [string match "-betterC" $arg] { + lappend out "-fno-druntime" + + } elseif { [string match "-boundscheck" $arg] + || [string match "-boundscheck=on" $arg] } { + lappend out "-fbounds-check" + + } elseif { [string match "-boundscheck=off" $arg] + || [string match "-noboundscheck" $arg] } { + lappend out "-fno-bounds-check" + + } elseif [string match "-boundscheck=safeonly" $arg] { + lappend out "-fbounds-check=safeonly" + + } elseif [string match "-c" $arg] { + lappend out "-c" + + } elseif [string match "-d" $arg] { + lappend out "-Wno-deprecated" + + } elseif [string match "-de" $arg] { + lappend out "-Wdeprecated" + lappend out "-Werror" + + } elseif [string match "-debug" $arg] { + lappend out "-fdebug" + + } elseif [regexp -- {^-debug=(\w+)} $arg pattern value] { + lappend out "-fdebug=$value" + + } elseif [string match "-dip1000" $arg] { + lappend out "-ftransition=dip1000" + + } elseif [string match "-dip25" $arg] { + lappend out "-ftransition=dip25" + + } elseif [string match "-dw" $arg] { + lappend out "-Wdeprecated" + lappend out "-Wno-error" + + } elseif [string match "-fPIC" $arg] { + lappend out "-fPIC" + + } elseif { [string match "-g" $arg] + || [string match "-gc" $arg] } { + lappend out "-g" + + } elseif [string match "-inline" $arg] { + lappend out "-finline-functions" + + } elseif [string match "-main" $arg] { + lappend out "-fmain" + + } elseif [regexp -- {^-mv=([\w+=./-]+)} $arg pattern value] { + lappend out "-fmodule-file=$value" + + } elseif [string match "-O" $arg] { + lappend out "-O2" + + } elseif [string match "-release" $arg] { + lappend out "-frelease" + + } elseif [regexp -- {^-transition=(\w+)} $arg pattern value] { + lappend out "-ftransition=$value" + + } elseif [string match "-unittest" $arg] { + lappend out "-funittest" + + } elseif [string match "-verrors=spec" $arg] { + lappend out "-Wspeculative" + + } elseif [regexp -- {^-verrors=(\d+)} $arg pattern num] { + lappend out "-fmax-errors=$num" + + } elseif [regexp -- {^-version=(\w+)} $arg pattern value] { + lappend out "-fversion=$value" + + } elseif [string match "-vtls" $arg] { + lappend out "-ftransition=tls" + + } elseif [string match "-w" $arg] { + lappend out "-Wall" + lappend out "-Werror" + + } elseif [string match "-wi" $arg] { + lappend out "-Wall" + lappend out "-Wno-error" + + } else { + # print "Unhandled Argument: $arg" + } + } + + return $out +} + +proc rust-copy-extra { base extra } { + # Split base, folder/file. + set type [file dirname $extra] + + # print "Filename: $base - $extra" + + set fdin [open $base/$extra r] + fconfigure $fdin -encoding binary + + file mkdir $type + set fdout [open $extra w] + fconfigure $fdout -encoding binary + + while { [gets $fdin copy_line] >= 0 } { + set out_line $copy_line + puts $fdout $out_line + } + + close $fdin + close $fdout + + # Remove file once test is finished. + upvar 2 cleanup_extra_files cleanups + lappend cleanups $extra + + return $extra +} + +# +# Translate DMD test directives to dejagnu equivalent. +# +# COMPILE_SEPARATELY: Not handled. +# EXECUTE_ARGS: Parameters to add to the execution of the test. +# COMPILED_IMPORTS: List of modules files that are imported by the main +# source file that should be included in compilation. +# Currently handled the same as EXTRA_SOURCES. +# EXTRA_SOURCES: List of extra sources to build and link along with +# the test. +# EXTRA_FILES: List of extra files to copy for the test runs. +# PERMUTE_ARGS: The set of arguments to permute in multiple compiler +# invocations. An empty set means only one permutation +# with no arguments. +# TEST_OUTPUT: The output expected from the compilation. +# POST_SCRIPT: Not handled. +# REQUIRED_ARGS: Arguments to add to the compiler command line. +# DISABLED: Not handled. +# + +proc dmd2dg { base test } { + global DEFAULT_DFLAGS + global PERMUTE_ARGS + global RUST_EXECUTE_ARGS + + set PERMUTE_ARGS $DEFAULT_DFLAGS + set RUST_EXECUTE_ARGS "" + + set extra_sources "" + set extra_files "" + + # Split base, folder/file. + set type [file dirname $test] + set name [file tail $test] + + print "Filename: $base - $test" + + set fdin [open $base/$test r] + #fconfigure $fdin -encoding binary + + file mkdir $type + set fdout [open $test w] + #fconfigure $fdout -encoding binary + + while { [gets $fdin copy_line] >= 0 } { + set out_line $copy_line + + if [regexp -- {COMPILE_SEPARATELY} $copy_line] { + # COMPILE_SEPARATELY is not handled. + regsub -- {COMPILE_SEPARATELY.*$} $copy_line "" out_line + + } elseif [regexp -- {DISABLED} $copy_line] { + # DISABLED is not handled. + regsub -- {DISABLED.*$} $copy_line "" out_line + + } elseif [regexp -- {POST_SCRIPT} $copy_line] { + # POST_SCRIPT is not handled + regsub -- {POST_SCRIPT.*$} $copy_line "" out_line + + } elseif [regexp -- {PERMUTE_ARGS\s*:\s*(.*)} $copy_line match args] { + # PERMUTE_ARGS is handled by rust-do-test. + set PERMUTE_ARGS [rust-convert-args $args] + regsub -- {PERMUTE_ARGS.*$} $copy_line "" out_line + + } elseif [regexp -- {EXECUTE_ARGS\s*:\s*(.*)} $copy_line match args] { + # EXECUTE_ARGS is handled by rust_load. + foreach arg $args { + lappend RUST_EXECUTE_ARGS $arg + } + regsub -- {EXECUTE_ARGS.*$} $copy_line "" out_line + + } elseif [regexp -- {REQUIRED_ARGS\s*:\s*(.*)} $copy_line match args] { + # Convert all listed arguments to from dmd to rust-style. + set new_option "{ dg-additional-options \"[rust-convert-args $args]\" }" + regsub -- {REQUIRED_ARGS.*$} $copy_line $new_option out_line + + } elseif [regexp -- {EXTRA_SOURCES\s*:\s*(.*)} $copy_line match sources] { + # EXTRA_SOURCES are appended to extra_sources list + foreach srcfile $sources { + lappend extra_sources $srcfile + } + regsub -- {EXTRA_SOURCES.*$} $copy_line "" out_line + + } elseif [regexp -- {EXTRA_CPP_SOURCES\s*:\s*(.*)} $copy_line match sources] { + # EXTRA_CPP_SOURCES are appended to extra_sources list + foreach srcfile $sources { + # C++ sources are found in the extra-files directory. + lappend extra_sources "extra-files/$srcfile" + } + regsub -- {EXTRA_CPP_SOURCES.*$} $copy_line "" out_line + + } elseif [regexp -- {EXTRA_FILES\s*:\s*(.*)} $copy_line match files] { + # EXTRA_FILES are appended to extra_files list + foreach file $files { + lappend extra_files $file + } + regsub -- {EXTRA_FILES.*$} $copy_line "" out_line + + } elseif [regexp -- {COMPILED_IMPORTS\s*:\s*(.*)} $copy_line match sources] { + # COMPILED_IMPORTS are appended to extra_sources list + foreach import $sources { + lappend extra_sources $import + } + regsub -- {COMPILED_IMPORTS.*$} $copy_line "" out_line + + } + + puts $fdout $out_line + } + + # Now that all extra sources and files have been collected, copy them all + # to the testsuite build directory. + if { [llength $extra_sources] > 0 } { + foreach srcfile $extra_sources { + rust-copy-extra $base "$type/$srcfile" + } + set out_line "// { dg-additional-sources \"$extra_sources\" }" + puts $fdout $out_line + } + + if { [llength $extra_files] > 0 } { + foreach file $extra_files { + rust-copy-extra $base "$type/$file" + } + set out_line "// { dg-additional-files \"$extra_files\" }" + puts $fdout $out_line + } + + # Add specific options for test type + + # DMD's testsuite is extremely verbose, compiler messages from constructs + # such as pragma(msg, ...) would otherwise cause tests to fail. + set out_line "// { dg-prune-output .* }" + puts $fdout $out_line + + # Compilable files are successful if an output is generated. + # Fail compilable are successful if an output is not generated. + # Runnable must compile, link, and return 0 to be successful by default. + switch $type { + runnable { + if ![isnative] { + set out_line "// { dg-final { output-exists } }" + puts $fdout $out_line + } + } + + compilable { + puts $fdout "// { dg-final { output-exists } }" + } + + fail_compilation { + puts $fdout "// { dg-do run { xfail *-*-* } }" + } + } + + close $fdin + close $fdout + + return $test +} + + +proc rust-permute-options { options } { + set result { } + set n [expr 1<<[llength $options]] + for { set i 0 } { $i<$n } { incr i } { + set option "" + for { set j 0 } { $j<[llength $options] } { incr j } { + if [expr $i & 1 << $j] { + append option [lindex $options $j] + append option " " + } + } + lappend result $option + + } + return $result +} + + +proc rust-do-test { } { + global srcdir subdir + global dg-do-what-default + global verbose + + # If a testcase doesn't have special options, use these. + global DEFAULT_DFLAGS + if ![info exists DEFAULT_DFLAGS] then { + set DEFAULT_DFLAGS "-g -O2" + } + + # These are special options to use on testcase, and override DEFAULT_DFLAGS + global PERMUTE_ARGS + + # Set if an extra option should be passed to link to shared druntime. + global SHARED_OPTION + + # Additional arguments for rust_load + global RUST_EXECUTE_ARGS + + # Initialize `dg'. + dg-init + + # Allow blank linkes in output for all of rust.test. + global allow_blank_lines + set save_allow_blank_lines $allow_blank_lines + if { !$allow_blank_lines } { + set allow_blank_lines 2 + } + + # Create rust.test link so test names include that subdir. + catch { file link $subdir . } + + # Main loop. + + # set verbose 1 + # set dg-final-code "" + # Find all tests and pass to routine. + foreach test [lsort [find $srcdir/$subdir *]] { + regexp -- "(.*)/(.+)/(.+)\.rs$" $test match base dir name ext + set ext "rs" + + # Skip invalid test extensions + if { [lsearch "rs" $ext] == -1 } { + continue + } + + # Convert to DG test. + set imports [format "-I%s/%s" $base $dir] + set cleanup_extra_files "" + # Include $subdir prefix so test names follow DejaGnu conventions. + set filename "$subdir/[dmd2dg $base $dir/$name.$ext]" + + if { $dir == "runnable" } { + append PERMUTE_ARGS " $SHARED_OPTION" + } + set options [rust-permute-options [lsort -unique $PERMUTE_ARGS]] + + switch $dir { + compilable { + for { set i 0 } { $i<[llength $options] } { incr i } { + set flags [lindex $options $i] + set dg-do-what-default "compile" + rust-dg-runtest $filename $flags $imports + } + } + + fail_compilation { + for { set i 0 } { $i<[llength $options] } { incr i } { + set flags [lindex $options $i] + set dg-do-what-default "compile" + rust-dg-runtest $filename $flags $imports + } + } + } + + # Cleanup test directory. + foreach srcfile $cleanup_extra_files { + file delete $subdir/$srcfile + } + file delete $filename + } + + set allow_blank_lines $save_allow_blank_lines + + # All done. + dg-finish +} + +rust-do-test + |