Skip to content

Commit

Permalink
Merge branch 'feature-expression-context-structs-construction'
Browse files Browse the repository at this point in the history
  • Loading branch information
Panzerschrek committed Apr 13, 2024
2 parents 03dca57 + aa8f03e commit 4dc6272
Show file tree
Hide file tree
Showing 22 changed files with 754 additions and 12 deletions.
2 changes: 2 additions & 0 deletions source/compiler0/code_builder_lib/cb_block_elements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ bool SingleExpressionIsUseless( const Synt::Expression& expression )
bool operator()( const std::unique_ptr<const Synt::IndexationOperator>& ) { return true; }
bool operator()( const std::unique_ptr<const Synt::MemberAccessOperator>& ) { return true; }
bool operator()( const std::unique_ptr<const Synt::MemberAccessOperatorCompletion>& ) { return true; }
// It's useless to initialize new variable and not using it.
bool operator()( const std::unique_ptr<const Synt::VariableInitialization>& ) { return true; }
// Await operator is basically an operator for an async call.
bool operator()( const std::unique_ptr<const Synt::AwaitOperator>& ) { return false; }
bool operator()( const std::unique_ptr<const Synt::UnaryPlus>& ) { return true; }
Expand Down
55 changes: 55 additions & 0 deletions source/compiler0/code_builder_lib/cb_expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,61 @@ Value CodeBuilder::BuildExpressionCodeImpl( NamesScope& names_scope, FunctionCon
return ErrorValue();
}

Value CodeBuilder::BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::VariableInitialization& variable_initialization )
{
const Value type_value= BuildExpressionCode( variable_initialization.type, names_scope, function_context );
CHECK_RETURN_ERROR_VALUE(type_value)

const Type* const type= type_value.GetTypeName();
if(type == nullptr)
{
REPORT_ERROR( NameIsNotTypeName, names_scope.GetErrors(), variable_initialization.src_loc, type_value.GetKindName() );
return ErrorValue();
}

if( !EnsureTypeComplete( *type ) )
{
REPORT_ERROR( UsingIncompleteType, names_scope.GetErrors(), variable_initialization.src_loc, type );
return ErrorValue();
}
else if( type->IsAbstract() )
REPORT_ERROR( ConstructingAbstractClassOrInterface, names_scope.GetErrors(), variable_initialization.src_loc, type );

const VariableMutPtr variable=
Variable::Create(
*type,
ValueType::Value,
Variable::Location::Pointer,
"temp " + type->ToString() );
function_context.variables_state.AddNode( variable );

if( !function_context.is_functionless_context )
{
variable->llvm_value= function_context.alloca_ir_builder.CreateAlloca( type->GetLLVMType() );
CreateLifetimeStart( function_context, variable->llvm_value );
}

{
const VariablePtr variable_for_initialization=
Variable::Create(
*type,
ValueType::ReferenceMut,
Variable::Location::Pointer,
variable->name,
variable->llvm_value );
function_context.variables_state.AddNode( variable_for_initialization );
function_context.variables_state.AddLink( variable, variable_for_initialization );
function_context.variables_state.TryAddInnerLinks( variable, variable_for_initialization, names_scope.GetErrors(), variable_initialization.src_loc );

variable->constexpr_value= ApplyInitializer( variable_for_initialization, names_scope, function_context, variable_initialization.initializer );

function_context.variables_state.RemoveNode( variable_for_initialization );
}

RegisterTemporaryVariable( function_context, variable );
return variable;
}

Value CodeBuilder::BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::AwaitOperator& await_operator )
{
return BuildAwait( names_scope, function_context, await_operator.expression, await_operator.src_loc );
Expand Down
2 changes: 1 addition & 1 deletion source/compiler0/code_builder_lib/cb_initializers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ llvm::Constant* CodeBuilder::ApplyInitializerImpl(

return expression_result->constexpr_value; // Move can preserve constexpr.
}
else
else if( !function_context.is_functionless_context )
{
llvm::Value* const value_for_copy=
CreateReferenceCast( expression_result->llvm_value, expression_result->type, variable->type, function_context );
Expand Down
1 change: 1 addition & 0 deletions source/compiler0/code_builder_lib/code_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ class CodeBuilder
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::IndexationOperator& indexation_operator );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::MemberAccessOperator& member_access_operator );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::MemberAccessOperatorCompletion& member_access_operator_completion );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::VariableInitialization& variable_initialization );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::AwaitOperator& await_operator );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::UnaryMinus& unary_minus );
Value BuildExpressionCodeImpl( NamesScope& names_scope, FunctionContext& function_context, const Synt::UnaryPlus& unary_plus );
Expand Down
5 changes: 5 additions & 0 deletions source/compiler0/lex_synt_lib/program_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,11 @@ void ElementWrite( const Expression& expression, std::ostream& stream )
ElementWrite( await_operator->expression, stream );
stream << "." << Keyword( Keywords::await_ );
}
void operator()( const std::unique_ptr<const VariableInitialization>& variable_initialization ) const
{
ElementWrite( variable_initialization->type, stream );
ElementWrite( variable_initialization->initializer, stream );
}
void operator()( const std::unique_ptr<const MemberAccessOperatorCompletion>& member_access_operator_completion ) const
{
ElementWrite( member_access_operator_completion->expression, stream );
Expand Down
10 changes: 10 additions & 0 deletions source/compiler0/lex_synt_lib/syntax_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,16 @@ Expression SyntaxAnalyzer::TryParseBinaryOperatorComponentPostfixOperator( Expre
return std::move(member_access_operator_completion);
}

case Lexem::Type::BraceLeft:
{
// Parse struct initializer for "{" in expression context.
auto variable_initialization= std::make_unique<VariableInitialization>( it_->src_loc );
variable_initialization->type= std::move(expr);
variable_initialization->initializer= ParseStructNamedInitializer();

return TryParseBinaryOperatorComponentPostfixOperator(std::move(variable_initialization));
}

default:
return expr;
};
Expand Down
12 changes: 12 additions & 0 deletions source/compiler0/lex_synt_lib/syntax_elements.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct CallOperatorSignatureHelp;
struct IndexationOperator;
struct MemberAccessOperator;
struct MemberAccessOperatorCompletion;
struct VariableInitialization;
struct AwaitOperator;
struct UnaryPlus;
struct UnaryMinus;
Expand Down Expand Up @@ -182,6 +183,7 @@ using Expression= std::variant<
std::unique_ptr<const IndexationOperator>,
std::unique_ptr<const MemberAccessOperator>,
std::unique_ptr<const MemberAccessOperatorCompletion>,
std::unique_ptr<const VariableInitialization>,
std::unique_ptr<const AwaitOperator>,
std::unique_ptr<const UnaryPlus>,
std::unique_ptr<const UnaryMinus>,
Expand Down Expand Up @@ -1239,6 +1241,16 @@ struct Lambda
Function function;
};

struct VariableInitialization
{
explicit VariableInitialization( const SrcLoc& src_loc )
: src_loc(src_loc) {}

SrcLoc src_loc;
Expression type;
Initializer initializer;
};

struct TypeAlias
{
explicit TypeAlias( const SrcLoc& src_loc )
Expand Down
2 changes: 2 additions & 0 deletions source/compiler1/code_builder_lib/cb_block_elements.u
Original file line number Diff line number Diff line change
Expand Up @@ -3228,6 +3228,8 @@ fn SingleExpressionIsUselessImpl( Synt::CallOperator& e ) : bool { ust::ignore_u
// It is useless to call such operators, even if they are overloaded, because logically these operators are created to produce some value.
fn SingleExpressionIsUselessImpl( Synt::IndexationOperator& e ) : bool { ust::ignore_unused(e); return true; }
fn SingleExpressionIsUselessImpl( Synt::MemberAccessOperator& e ) : bool { ust::ignore_unused(e); return true; }
// It's useless to initialize new variable and not using it.
fn SingleExpressionIsUselessImpl( Synt::VariableInitialization& e ) : bool { ust::ignore_unused(e); return true; }
// Await operator is basically an operator for an async call.
fn SingleExpressionIsUselessImpl( Synt::AwaitOperator& e ) : bool { ust::ignore_unused(e); return false; }
fn SingleExpressionIsUselessImpl( Synt::UnaryPlus& e ) : bool { ust::ignore_unused(e); return true; }
Expand Down
74 changes: 74 additions & 0 deletions source/compiler1/code_builder_lib/cb_expressions.u
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,80 @@ fn CodeBuilder::BuildExpressionCode( mut this, NamesScopePtr& names_scope, Funct
return ErrorValue();
}

fn CodeBuilder::BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::VariableInitialization& variable_initialization ) : Value
{
var Value type_value= BuildExpressionCode( names_scope, function_context, variable_initialization.type_name.deref() );
auto type_name_opt= type_value.get</Type/>();
if(type_name_opt.empty())
{
REPORT_ERROR( NameIsNotTypeName, names_scope, variable_initialization.src_loc, GetValueKindName(type_value) )
return ErrorValue();
}

var Type& t= type_name_opt.try_deref();

if( !EnsureTypeComplete( t ) )
{
REPORT_ERROR( UsingIncompleteType, names_scope, variable_initialization.src_loc, t )
return ErrorValue();
}
if( t.IsAbstract() )
{
REPORT_ERROR( ConstructingAbstractClassOrInterface, names_scope, variable_initialization.src_loc, t )
return ErrorValue();
}

var ust::string8 mut name= "temp " + t.ToString();

var Variable mut result
{
.t= t,
.location= Variable::Location::Pointer,
.value_type= ValueType::Value,
.name= name,
};

auto mut llvm_value= LLVMValueRef::Null;
if( !function_context.is_functionless_context )
{
name += "\0";
llvm_value= unsafe( LLVMBuildAlloca( function_context.alloca_ir_builder, t.GetLLVMType(), name.front() ) );
CreateLifetimeStart( function_context, llvm_value );
}
result.llvm_value= llvm_value;

var VariablePtr result_ptr= move(result).CreatePtr();

function_context.references_graph.AddNode( result_ptr );

{
var Variable mut variable_for_initialization
{
.t= t,
.value_type= ValueType::ReferenceMut,
.location= Variable::Location::Pointer,
.llvm_value= llvm_value,
.name= name,
};
var VariablePtr variable_for_initialization_ptr= move(variable_for_initialization).CreatePtr();
function_context.references_graph.AddNode( variable_for_initialization_ptr );
function_context.references_graph.AddLink( result_ptr, variable_for_initialization_ptr );
function_context.references_graph.TryAddInnerLinks( result_ptr, variable_for_initialization_ptr, names_scope, variable_initialization.src_loc );

var LLVMValueRef constexpr_value= BuildInitializer( names_scope, function_context, variable_for_initialization_ptr, variable_initialization.initializer.deref() );

function_context.references_graph.RemoveNode( variable_for_initialization_ptr );

with( mut lock : result_ptr.lock_mut() )
{
lock.deref().constexpr_value= constexpr_value;
}
}

RegisterTemporaryVariable( function_context, result_ptr );
return result_ptr;
}

fn CodeBuilder::BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::AwaitOperator& await_operator ) : Value
{
return BuildAwait( names_scope, function_context, await_operator.value.deref(), await_operator.src_loc );
Expand Down
28 changes: 20 additions & 8 deletions source/compiler1/code_builder_lib/cb_initializers.u
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,11 @@ fn CodeBuilder::BuildInitializer( mut this, NamesScopePtr& names_scope, Function
// Move.
function_context.references_graph.MoveNode( expr_result_ptr );

CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
if( !function_context.is_functionless_context )
{
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
}
}
else
{
Expand Down Expand Up @@ -101,8 +104,11 @@ fn CodeBuilder::BuildInitializer( mut this, NamesScopePtr& names_scope, Function
{
// Move.
function_context.references_graph.MoveNode( expr_result_ptr );
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
if( !function_context.is_functionless_context )
{
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
}
}
else if( !function_context.is_functionless_context )
{
Expand Down Expand Up @@ -1004,8 +1010,11 @@ fn CodeBuilder::BuildConstructorInitializer( mut this, NamesScopePtr& names_scop
{
// Move.
function_context.references_graph.MoveNode( expr_result_ptr );
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
if( !function_context.is_functionless_context )
{
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
}
}
else
{
Expand Down Expand Up @@ -1096,8 +1105,11 @@ fn CodeBuilder::BuildConstructorInitializer( mut this, NamesScopePtr& names_scop

var VariableLite expr_result= expr_result_ptr.lock_imut().deref();

CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
if( !function_context.is_functionless_context )
{
CopyBytes( variable.llvm_value, expr_result.llvm_value, variable.t, function_context );
CreateLifetimeEnd( function_context, expr_result.llvm_value );
}

return expr_result.constexpr_value; // Move can preserve constexpr.
}
Expand Down
1 change: 1 addition & 0 deletions source/compiler1/code_builder_lib/code_builder.uh
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ private:
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::IndexationOperator& indexation_operator ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::CallOperator& call_operator ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::MemberAccessOperator& member_access_operator ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::VariableInitialization& variable_initialization ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::AwaitOperator& await_operator ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::UnaryPlus& unary_plus ) : Value;
fn BuildExpressionCode( mut this, NamesScopePtr& names_scope, FunctionContext &mut function_context, Synt::UnaryMinus& unary_minus ) : Value;
Expand Down
10 changes: 10 additions & 0 deletions source/compiler1/lex_synt_lib/syntax_analyzer.u
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,16 @@ fn SyntaxAnalyzer::TryParseBinaryOperatorComponentPostfixOperator( mut this, Exp
return TryParseBinaryOperatorComponentPostfixOperator(move(member_access_operator));
}
},
Lexem::Type::BraceLeft ->
{
var VariableInitialization mut variable_initialization
{
.src_loc= it_.front().src_loc,
.type_name(move(expr)),
.initializer(ParseInitializer(false)),
};
return TryParseBinaryOperatorComponentPostfixOperator(move(variable_initialization));
},
default -> { return expr; },
}
}
Expand Down
8 changes: 8 additions & 0 deletions source/compiler1/lex_synt_lib/syntax_elements.uh
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,13 @@ struct MemberAccessOperator
ust::box_nullable</TemplateArgs/> template_args;
}

struct VariableInitialization
{
SrcLoc src_loc;
ust::box</Expression/> type_name;
ust::box</Initializer/> initializer;
}

struct AwaitOperator
{
SrcLoc src_loc;
Expand Down Expand Up @@ -293,6 +300,7 @@ type Expression= ust::variant</ tup[
IndexationOperator,
CallOperator,
MemberAccessOperator,
VariableInitialization,
AwaitOperator,
UnaryPlus,
UnaryMinus,
Expand Down
12 changes: 12 additions & 0 deletions source/docs/en/initializers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ Each field is specified starting with ``.``.
var tup[ S ] t[ { .z= 0, .x= 2, .y= 2 } ];
}
It's possible to use this initializer kind in expression context in order to construct temporary values of struct types.

.. code-block:: u_spr
struct S{ i32 x; i32 y; }
fn Bar(S s);
fn Foo()
{
Bar( S{ .x= 42, .y= 24 } ); // Create temporary value of "S" type by initializing its fields, than pass it into a function.
}
**********************
*Empty initialization*
**********************
Expand Down
11 changes: 11 additions & 0 deletions source/docs/ru/initializers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@
var tup[ S ] t[ { .z= 0, .x= 2, .y= 2 } ];
}
Данный инициализатор можно использовать также в контексте выражения для создания временных значений типов структур.

.. code-block:: u_spr
struct S{ i32 x; i32 y; }
fn Bar(S s);
fn Foo()
{
Bar( S{ .x= 42, .y= 24 } ); // Создаём временное значение типа "S", инициализируя его поля, после чего передаём его в функцию.
}
**********************
*Пустая инициализация*
**********************
Expand Down
6 changes: 6 additions & 0 deletions source/language_server/syntax_tree_lookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ void FindImpl( const Synt::MemberAccessOperatorCompletion& member_access_operato
}
}

void FindImpl( const Synt::VariableInitialization& variable_initialization )
{
FindImpl( variable_initialization.type );
FindImpl( variable_initialization.initializer );
}

void FindImpl( const Synt::AwaitOperator& await_operator )
{
FindImpl( await_operator.expression );
Expand Down
Loading

0 comments on commit 4dc6272

Please sign in to comment.