Skip to content

Commit 57e638b

Browse files
committed
Fixed array assignability checking
1 parent 97649ae commit 57e638b

File tree

8 files changed

+96
-222
lines changed

8 files changed

+96
-222
lines changed

proto/autotest/lang/arrayinittest.tx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ array_stack_construction( i : UByte ) {
9292
a44 := MyFA( [ 1.1, 2.2 ] );
9393

9494

95-
## TODO: support partial initialization of explicitly typed array literals (relevant iff modifiable assignee)
95+
## FUTURE: support partial initialization of explicitly typed array literals
96+
## (should be seen as array type conversion constructors)
9697
##a45 := [2]Int( [ 10I ] );
9798
#experr 1: ae := [3]Int( 1 );
9899
#experr 1: ae := [3]Int( 1, 2 );

proto/src/ast/expr/ast_array.cpp

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ TxFilledArrayLitNode::TxFilledArrayLitNode( const TxLocation& ploc, TxQualTypeEx
5555
}
5656

5757
TxQualType TxFilledArrayLitNode::define_type( TxPassInfo passInfo ) {
58-
const TxActualType* expectedArgType;
58+
TxQualType expectedArgQType;
5959
const TxActualType* arrayType = nullptr;
60+
61+
auto elemTypeNode = this->elementTypeNode;
62+
if ( !elemTypeNode ) {
63+
elemTypeNode = new TxTypeTypeArgumentNode( new TxQualTypeExprNode( new TxTypeExprWrapperNode( elemExprList->front()->originalExpr ) ) );
64+
run_declaration_pass( elemTypeNode, this, "elem-type" );
65+
}
66+
TxQualType elemQType = elemTypeNode->typeExprNode->resolve_type( passInfo );
67+
6068
if ( this->capacityExpr ) {
61-
auto elemTypeNode = this->elementTypeNode;
62-
if ( !elemTypeNode ) {
63-
elemTypeNode = new TxTypeTypeArgumentNode( new TxQualTypeExprNode( new TxTypeExprWrapperNode( elemExprList->front()->originalExpr ) ) );
64-
run_declaration_pass( elemTypeNode, this, "elem-type" );
65-
}
6669
this->capacityExpr->insert_conversion( passInfo, this->registry().get_builtin_type( ARRAY_SUBSCRIPT_TYPE_ID ) );
6770
auto capacityNode = new TxValueTypeArgumentNode( this->capacityExpr );
6871
capacityNode->node_declaration_pass( this );
@@ -72,10 +75,10 @@ TxQualType TxFilledArrayLitNode::define_type( TxPassInfo passInfo ) {
7275
&& get_reinterpretation_degree( this->elemExprList->front()->originalExpr, arrayType ) >= 0 ) {
7376
// treat as array to array assignment
7477
this->_directArrayArg = true;
75-
expectedArgType = arrayType;
78+
expectedArgQType = arrayType;
7679
}
7780
else {
78-
expectedArgType = elemExprList->front()->originalExpr->resolve_type( passInfo ).type();
81+
expectedArgQType = elemQType;
7982
}
8083
}
8184
else {
@@ -92,29 +95,23 @@ TxQualType TxFilledArrayLitNode::define_type( TxPassInfo passInfo ) {
9295
// concrete array capacity - treat as array to array assignment
9396
this->_directArrayArg = true;
9497
arrayType = singleArgType;
95-
expectedArgType = arrayType;
98+
expectedArgQType = arrayType;
9699
}
97100
}
98101
}
99102
}
100103

101104
if ( !arrayType ) {
102-
auto elemTypeNode = this->elementTypeNode;
103-
if ( !elemTypeNode ) {
104-
elemTypeNode = new TxTypeTypeArgumentNode( new TxQualTypeExprNode( new TxTypeExprWrapperNode( elemExprList->front()->originalExpr ) ) );
105-
run_declaration_pass( elemTypeNode, this, "elem-type" );
106-
}
107105
auto tmpcapacityExpr = new TxIntegerLitNode( this->ploc, elemExprList->size(), false, ARRAY_SUBSCRIPT_TYPE_ID );
108106
auto capacityNode = new TxValueTypeArgumentNode( tmpcapacityExpr );
109107
run_declaration_pass( capacityNode, this, "capacity" );
110108
arrayType = this->registry().get_array_type( this, elemTypeNode, capacityNode );
111-
expectedArgType = elemTypeNode->typeExprNode->resolve_type( passInfo ).type(); //arrayEntType->element_type().type();
112-
109+
expectedArgQType = elemQType;
113110
}
114111
}
115112

116113
for ( auto elemExpr : *this->elemExprList )
117-
elemExpr->insert_conversion( passInfo, expectedArgType );
114+
elemExpr->insert_conversion( passInfo, expectedArgQType );
118115
return arrayType;
119116
}
120117

@@ -132,12 +129,7 @@ void TxFilledArrayLitNode::resolution_pass() {
132129
void TxFilledArrayLitNode::verification_pass() const {
133130
if ( this->capacityExpr && this->capacityExpr->attempt_qtype() ) {
134131
if ( this->capacityExpr->is_statically_constant() ) {
135-
if ( this->elemExprList->size() == 1
136-
&& this->elemExprList->front()->attempt_qtype()
137-
&& this->elemExprList->front()->attempt_qtype()->is_assignable_to( *this->qtype() ) ) {
138-
// array to array assignment
139-
}
140-
else if ( eval_unsigned_int_constant( this->capacityExpr ) != this->elemExprList->size() )
132+
if ( !this->_directArrayArg && eval_unsigned_int_constant( this->capacityExpr ) != this->elemExprList->size() )
141133
CERROR( this, "Capacity expression of array literal equals " << eval_unsigned_int_constant( this->capacityExpr )
142134
<< ", but number of elements is " << this->elemExprList->size() );
143135
}

proto/src/ast/expr/ast_array_codegen.cpp

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,44 +29,23 @@ Value* TxFilledArrayLitNode::code_gen_dyn_value( LlvmGenerationContext& context,
2929
return this->elemExprList->front()->code_gen_dyn_value( context, scope );
3030
}
3131

32-
{
33-
Type* arrayObjT = context.get_llvm_type( this->qtype() );
34-
Value* arrayObjV = UndefValue::get( arrayObjT );
35-
auto capacityC = ConstantInt::get( Type::getInt32Ty( context.llvmContext ), this->elemExprList->size() );
36-
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, capacityC, 0 );
37-
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, capacityC, 1 ); // length equals capacity
38-
39-
if ( this->elemExprList->empty() ) {
40-
Constant* emptyArrayC = ConstantArray::get( cast<ArrayType>( arrayObjT->getContainedType( 2 ) ), ArrayRef<Constant*>() );
41-
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, emptyArrayC, 2 );
42-
}
43-
else {
44-
for ( unsigned i = 0; i < this->elemExprList->size(); i++ ) {
45-
auto elemV = this->elemExprList->at( i )->code_gen_dyn_value( context, scope );
46-
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, elemV, std::vector<unsigned>( { 2, i } ) );
47-
}
48-
}
49-
return arrayObjV;
50-
}
32+
Type* arrayObjT = context.get_llvm_type( this->qtype() );
33+
Value* arrayObjV = UndefValue::get( arrayObjT );
34+
auto capacityC = ConstantInt::get( Type::getInt32Ty( context.llvmContext ), this->elemExprList->size() );
35+
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, capacityC, 0 );
36+
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, capacityC, 1 ); // length equals capacity
5137

52-
/*
53-
Value* arrayPtrV = this->get_type()->type()->gen_alloca( context, scope, "array_lit" );
54-
{ // initialize length field:
55-
Value* ixs[] = { ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ),
56-
ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 1 ) };
57-
auto lenField = scope->builder->CreateInBoundsGEP( arrayPtrV, ixs );
58-
auto lenVal = ConstantInt::get( Type::getInt32Ty( context.llvmContext ), this->elemExprList->size() );
59-
scope->builder->CreateStore( lenVal, lenField );
38+
if ( this->elemExprList->empty() ) {
39+
Constant* emptyArrayC = ConstantArray::get( cast<ArrayType>( arrayObjT->getContainedType( 2 ) ), ArrayRef<Constant*>() );
40+
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, emptyArrayC, 2 );
6041
}
61-
for ( unsigned i = 0; i < this->elemExprList->size(); i++ ) {
62-
Value* ixs[] = { ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 0 ),
63-
ConstantInt::get( Type::getInt32Ty( context.llvmContext ), 2 ),
64-
ConstantInt::get( Type::getInt32Ty( context.llvmContext ), i ) };
65-
auto elemAddr = scope->builder->CreateInBoundsGEP( arrayPtrV, ixs );
66-
scope->builder->CreateStore( this->elemExprList->at( i )->code_gen_value( context, scope ), elemAddr );
42+
else {
43+
for ( unsigned i = 0; i < this->elemExprList->size(); i++ ) {
44+
auto elemV = this->elemExprList->at( i )->code_gen_dyn_value( context, scope );
45+
arrayObjV = scope->builder->CreateInsertValue( arrayObjV, elemV, std::vector<unsigned>( { 2, i } ) );
46+
}
6747
}
68-
return arrayPtrV;
69-
*/
48+
return arrayObjV;
7049
}
7150

7251
Constant* TxFilledArrayLitNode::code_gen_const_value( LlvmGenerationContext& context ) const {

proto/src/symbol/type_class_handler.cpp

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,42 +45,42 @@ bool TxTypeClassHandler::auto_converts_to( const TxActualType* type, const TxAct
4545

4646
/*=== Array and Reference handlers ===*/
4747

48-
bool TxArrayTypeClassHandler::auto_converts_to( const TxActualType* type, const TxActualType* destination ) const {
49-
if ( this != destination->type_class_handler() )
50-
return false;
51-
if ( this->inner_equals( type, destination ) )
52-
return true;
53-
54-
// (Array conversion check is stricter than array assignability check, since array assignment code will check
55-
// lengths in runtime, whereas e.g. ref-to-array conversions can't provide that guarantee.)
56-
// if origin has unbound type params that destination does not, origin is more generic and can't be converted to destination
57-
if ( auto toElem = destination->element_type() ) {
58-
if ( auto fromElem = type->element_type() ) {
59-
// note: is-a test insufficient for array elements, since assignable type (same instance data type) required
60-
if ( !fromElem->is_assignable_to( *toElem ) )
61-
return false;
62-
}
63-
else
64-
return false; // origin has not bound E
65-
}
66-
if ( auto lenExpr = destination->capacity() ) {
67-
if ( auto fromLenExpr = type->capacity() ) {
68-
return ( lenExpr->is_statically_constant() && fromLenExpr->is_statically_constant()
69-
&& ( eval_unsigned_int_constant( lenExpr ) == eval_unsigned_int_constant( fromLenExpr ) ) );
70-
}
71-
else
72-
return false; // origin has not bound C
73-
}
74-
return true;
75-
}
48+
//bool TxArrayTypeClassHandler::auto_converts_to( const TxActualType* type, const TxActualType* destination ) const {
49+
// if ( this != destination->type_class_handler() )
50+
// return false;
51+
// if ( this->inner_equals( type, destination ) )
52+
// return true;
53+
//
54+
// // if origin has unbound type params that destination does not, origin is more generic and can't be converted to destination
55+
// if ( auto toElem = destination->element_type() ) {
56+
// if ( auto fromElem = type->element_type() ) {
57+
// // note: is-a test insufficient for array elements, since assignable type (same instance data type) required
58+
// if ( !fromElem->is_assignable_to( *toElem ) )
59+
// return false;
60+
// }
61+
// else
62+
// return false; // origin has not bound E
63+
// }
64+
// if ( auto lenExpr = destination->capacity() ) {
65+
// if ( auto fromLenExpr = type->capacity() ) {
66+
// return ( lenExpr->is_statically_constant() && fromLenExpr->is_statically_constant()
67+
// && ( eval_unsigned_int_constant( lenExpr ) == eval_unsigned_int_constant( fromLenExpr ) ) );
68+
// }
69+
// else
70+
// return false; // origin has not bound C
71+
// }
72+
// return true;
73+
//}
7674

7775
bool TxArrayTypeClassHandler::inner_is_assignable_to( const TxActualType* type, const TxActualType* destination ) const {
7876
ASSERT( destination->get_type_class() == TXTC_ARRAY, "destination not an array: " << destination );
7977
if ( this->inner_equals( type, destination ) )
8078
return true;
8179

82-
// This implementation assumes that if lengths/capacities are not statically known, code will be generated
83-
// that checks this in runtime.
80+
// This check is strict and requires statically verifiable assignability.
81+
// (e.g. ref-to-array conversions need that guarantee)
82+
// If origin has unbound type params that destination does not, origin is more generic and can't be converted to destination.
83+
// (For assignments where lengths/capacities are not statically known, code must be generated to check this in runtime.)
8484
if ( auto toElem = destination->element_type() ) {
8585
if ( auto fromElem = type->element_type() ) {
8686
// note: is-a test insufficient for array elements, since assignable type (same instance data type) required
@@ -92,15 +92,12 @@ bool TxArrayTypeClassHandler::inner_is_assignable_to( const TxActualType* type,
9292
}
9393
if ( auto lenExpr = destination->capacity() ) {
9494
if ( auto fromLenExpr = type->capacity() ) {
95-
if ( lenExpr->is_statically_constant() && fromLenExpr->is_statically_constant() ) {
96-
// TODO: ideally we should check source Length instead of source Capacity
97-
return ( eval_unsigned_int_constant( lenExpr ) >= eval_unsigned_int_constant( fromLenExpr ) );
98-
}
99-
else
100-
return true; // dynamic capacities must be checked in runtime
95+
// TODO: ideally we should check source Length instead of source Capacity
96+
return ( lenExpr->is_statically_constant() && fromLenExpr->is_statically_constant()
97+
&& ( eval_unsigned_int_constant( lenExpr ) == eval_unsigned_int_constant( fromLenExpr ) ) );
10198
}
10299
else
103-
return true; // origin has not bound C, dynamic capacities must be checked in runtime
100+
return false; // origin has not bound C, dynamic capacities must be checked in runtime
104101
}
105102
return true;
106103
}

0 commit comments

Comments
 (0)