Artifact c1223d2d9f50adaa868b018da9487c693692825c9564bb6f04aa84f063dc455e:
- File
bs/builtins/operators/assignment.cpp
— part of check-in
[9b058524b4]
at
2021-02-18 19:35:07
on branch trunk
— Implemented reference initialization, so reference local variables can now be declared, and added a typechecking rule preventing template variables to bind to reference types.
Then fixed a MILLION horrible problems caused by all that. Now almost everything works again. *sobs* (user: achavasse size: 7855)
#include "builtins/builtins.h" #include "precedence.h" #include "helpers.h" #include "tuple.h" using namespace goose; using namespace goose::eir; using namespace goose::cir; using namespace goose::parse; namespace goose::builtins { void SetupAssignmentOps( Env& e ) { auto assOp = GetOrCreateOverloadSet( e, "operator_assign"_sid ); // Generic function to perform the assignation of a builtin runtime type // to a mutable reference of that same type. auto BuiltinTypeAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value { G_VAL_ASSERT( lhs, !lhs.isConstant() ); auto refType = *FromValue< ReferenceType >( *ValueFromEIR( lhs.type() ) ); if( !ParseTypePredicates( c, c.identity(), *ValueFromEIR( refType.type() ) ) ) return PoisonValue(); const auto& cb = c.codeBuilder(); if( !cb ) { DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." ); return PoisonValue(); } if( auto bb = cb->cfg()->currentBB() ) bb->emplace_back( Store( lhs.cir(), rhs ) ); // Return the ref to support chained assignments, like in c++. return Value( lhs ); }; // Generic function to assign a value to a decl (local variable declaration) auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value { if( !c.codeBuilder() ) { DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." ); return PoisonValue(); } auto decl = *FromValue< Decl >( lhs ); return DeclareLocalVar( c, decl.type(), decl.name(), rhs ); }; // Generic function to assign a value to a template named decl (local variable declaration with local type inference) auto TNamedDeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value { if( !c.codeBuilder() ) { DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "variable declarations are not allowed here." ); return PoisonValue(); } auto tndecl = *FromValue< TNamedDecl >( lhs ); return DeclareLocalVarWithTypeInference( c, tndecl.type(), tndecl.name(), rhs ); }; // Tuple assignment. // // We create anonymous variables with type inference, and initialize them with // the values form the rhs tuple. Then we assign each of them to members of the lhs tuple. // // The reason we do all this is so that cases such as // a,b = b,a work as expected. // // We count on llvm to elide the temp vars in case they are not needed, but then again // we do already count on it to elide all those load and stores and allocas anyway. // // The real performance overhead danger here is for complex type whose InitializeVar() // and Destroy() implementations emit code that can't be elided and that may generate object // copies. // // TODO: Ideally, we should restrict tuple assignation only to types that can be moved. // For types that can only be copied, it may be preferable to write it the old fashioned // way with a temp var to make the copy more apparent. // But complex types and the notion of moving them don't even exist yet. auto TupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value { auto tupSize = TupleSize( lhs ); if( tupSize != TupleSize( rhs ) ) { DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." ); return PoisonValue(); } // Load the values from the rhs tuple into temporary vars. vector< Value > tempVars; tempVars.reserve( tupSize ); bool success = true; ForEachInTuple( rhs, [&]( auto&& srcVal ) { // We go at very high level to construct the temporary variables by resolving an invocation // of the assignment of the rhs value to a TNamedDecl with an empty name. // The reason we need to work at such high level is so that the initializer value gets // resolved exactly like if it was a regular assignment. In particular, if the value in question // is a locvar, we want it to go through the type checking process to be replaced with its content. // This is the simplest and most robust way to achieve this, which should honor every relevant // extension point. auto anonVarDecl = ToValue( TNamedDecl( ValueToEIR( ToValue( TVar( "_"_sid ) ) ), ""_sid ) ); auto tmpVar = InvokeOverloadSet( c, assOp, MakeTuple( anonVarDecl, srcVal ) ); if( tmpVar.isPoison() ) { success = false; return false; } tempVars.emplace_back( move( tmpVar ) ); return true; } ); if( !success ) return PoisonValue(); auto result = EmptyTuple(); size_t i = 0; ForEachInTuple( lhs, [&]( auto&& destVal ) { auto r = InvokeOverloadSet( c, assOp, MakeTuple( destVal, tempVars[i++] ) ); if( r.isPoison() ) { success = false; return false; } result = AppendToTuple( result, move( r ) ); return true; } ); if( !success ) return PoisonValue(); return result; }; BuildParseRule( e, "="_sid, RightAssInfixOp( "operator_assign"_sid, precedence::AssignmentOp, // Assignment with a decl of type $T to the left and a value of type $T to the right: // Local variable declaration and initialization. ForTypes< CustomPattern< Decl, Decl::Pattern >, CustomPattern< Value, ForwarderPattern > >( DeclAssignment ), // Assignment with a tnameddecl to the left and a value to the right: // Local variable declaration and initialization with type inference. ForTypes< CustomPattern< TNamedDecl, TNamedDecl::Pattern >, CustomPattern< Value, ForwarderPattern > >( TNamedDeclAssignment ), ForType< CustomPattern< Value, TuplePattern > >( TupleAssignment ), // Assignment of a mutable ref. // We define overloads for every runtime builtin types and directly perform the assignation. ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOfType< bool > >, bool >( BuiltinTypeAssignment ), ForTypes< CustomPattern< Value, ReferenceType::PatternMutable< IntegerType::Pattern > >, CustomPattern< Value, IntegerType::Pattern > >( BuiltinTypeAssignment ), ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOfType< BigInt > >, BigInt >( BuiltinTypeAssignment ), ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOfType< char32_t > >, char32_t >( BuiltinTypeAssignment ), ForTypes< CustomPattern< Value, ReferenceType::PatternMutableOfType< string > >, string >( BuiltinTypeAssignment ) ) ); } }