Bug fixes for layer expression processing.

1) Push a VAR onto the stack, not a resolved value
2) Don't collapse a PCB_LAYER_VALUE to a VALUE during processing
3) Make sure we run overloaded operators from the correct side

Fixes https://gitlab.com/kicad/code/kicad/issues/12437

(cherry picked from commit cf1565a16a)
This commit is contained in:
Jeff Young 2022-09-16 14:13:34 +01:00
parent e8a10939b5
commit 7dfa715a45
6 changed files with 383 additions and 37 deletions

View File

@ -1038,7 +1038,7 @@ bool COMPILER::generateUCode( UCODE* aCode, CONTEXT* aPreflightContext )
reportError( CST_CODEGEN, msg, node->srcPos - (int) node->value.str->length() );
}
node->SetUop( TR_UOP_PUSH_VALUE, std::move( vref ) );
node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
node->isTerminal = true;
break;
}
@ -1090,10 +1090,12 @@ void UOP::Exec( CONTEXT* ctx )
{
case TR_UOP_PUSH_VAR:
{
VALUE* value = ctx->AllocValue();
VALUE* value = nullptr;
if( m_ref )
value->Set( m_ref->GetValue( ctx ) );
value = ctx->StoreValue( m_ref->GetValue( ctx ) );
else
value = ctx->AllocValue();
ctx->Push( value );
}
@ -1162,10 +1164,20 @@ void UOP::Exec( CONTEXT* ctx )
result = arg1Value > arg2Value ? 1 : 0;
break;
case TR_OP_EQUAL:
result = arg1 && arg2 && arg1->EqualTo( ctx, arg2 ) ? 1 : 0;
if( !arg1 || !arg2 )
result = arg1 == arg2 ? 1 : 0;
else if( arg2->GetType() == VT_UNDEFINED )
result = arg2->EqualTo( ctx, arg1 ) ? 1 : 0;
else
result = arg1->EqualTo( ctx, arg2 ) ? 1 : 0;
break;
case TR_OP_NOT_EQUAL:
result = arg1 && arg2 && arg1->NotEqualTo( ctx, arg2 ) ? 1 : 0;
if( !arg1 || !arg2 )
result = arg1 != arg2 ? 1 : 0;
else if( arg2->GetType() == VT_UNDEFINED )
result = arg2->NotEqualTo( ctx, arg1 ) ? 1 : 0;
else
result = arg1->NotEqualTo( ctx, arg2 ) ? 1 : 0;
break;
case TR_OP_BOOL_AND:
result = arg1Value != 0.0 && arg2Value != 0.0 ? 1 : 0;

View File

@ -277,7 +277,7 @@ public:
virtual ~VAR_REF() {};
virtual VAR_TYPE_T GetType() const = 0;
virtual VALUE GetValue( CONTEXT* aCtx ) = 0;
virtual VALUE* GetValue( CONTEXT* aCtx ) = 0;
};
@ -293,7 +293,7 @@ public:
virtual ~CONTEXT()
{
for( auto &v : m_ownedValues )
for( VALUE* v : m_ownedValues )
{
delete v;
}
@ -305,6 +305,12 @@ public:
return m_ownedValues.back();
}
VALUE* StoreValue( VALUE* aValue )
{
m_ownedValues.emplace_back( aValue );
return m_ownedValues.back();
}
void Push( VALUE* v )
{
m_stack[ m_stackPtr++ ] = v;

View File

@ -39,6 +39,14 @@
<br><br>
### Items
* `A` &nbsp;&nbsp; _the first (or only) item under test_
* `B` &nbsp;&nbsp; _the second item under test (for binary tests)_
* `L` &nbsp;&nbsp; _the layer currently under test_
<br>
### Item Types
* buried_via

View File

@ -0,0 +1,314 @@
// Do not edit this file, it is autogenerated by CMake from the .md file
_HKI( "### Top-level Clauses\n"
"\n"
" (version <number>)\n"
"\n"
" (rule <rule_name> <rule_clause> ...)\n"
"\n"
"\n"
"<br>\n"
"\n"
"### Rule Clauses\n"
"\n"
" (constraint <constraint_type> ...)\n"
"\n"
" (condition \"<expression>\")\n"
"\n"
" (layer \"<layer_name>\")\n"
"\n"
" (severity <severity_name>)\n"
"\n"
"\n"
"<br>\n"
"\n"
"### Constraint Types\n"
"\n"
" * annular\\_width\n"
" * clearance\n"
" * connection\\_width\n"
" * courtyard_clearance\n"
" * diff\\_pair\\_gap\n"
" * diff\\_pair\\_uncoupled\n"
" * disallow\n"
" * edge\\_clearance\n"
" * length\n"
" * hole\\_clearance\n"
" * hole\\_size\n"
" * min\\_resolved\\_spokes\n"
" * physical\\_clearance\n"
" * physical\\_hole\\_clearance\n"
" * silk\\_clearance\n"
" * skew\n"
" * text\\_height\n"
" * text\\_thickness\n"
" * thermal\\_relief\\_gap\n"
" * thermal\\_spoke\\_width\n"
" * track\\_width\n"
" * via\\_count\n"
" * via\\_diameter\n"
" * zone\\_connection\n"
"\n"
"Note: `clearance` and `hole_clearance` rules are not run against items of the same net; `physical_clearance` and `physical_hole_clearance` rules are.\n"
"<br><br>\n"
"\n"
"### Items\n"
"\n"
" * `A` &nbsp;&nbsp; _the first (or only) item under test_\n"
" * `B` &nbsp;&nbsp; _the second item under test (for binary tests)_\n"
" * `L` &nbsp;&nbsp; _the layer currently under test_\n"
"\n"
"<br>\n"
"\n"
"### Item Types\n"
"\n"
" * buried\\_via\n"
" * graphic\n"
" * hole\n"
" * micro\\_via\n"
" * pad\n"
" * text\n"
" * track\n"
" * via\n"
" * zone\n"
"\n"
"<br>\n"
"\n"
"### Zone Connections\n"
"\n"
" * solid\n"
" * thermal\\_reliefs\n"
" * none\n"
"\n"
"<br>\n"
"\n"
"### Severity Names\n"
"\n"
" * warning\n"
" * error\n"
" * exclusion\n"
" * ignore\n"
"\n"
"<br>\n"
"\n"
"### Examples\n"
"\n"
" (version 1)\n"
"\n"
" (rule HV\n"
" (constraint clearance (min 1.5mm))\n"
" (condition \"A.NetClass == 'HV'\"))\n"
"\n"
"\n"
" (rule HV\n"
" (layer outer)\n"
" (constraint clearance (min 1.5mm))\n"
" (condition \"A.NetClass == 'HV'\"))\n"
"\n"
"\n"
" (rule HV_HV\n"
" # wider clearance between HV tracks\n"
" (constraint clearance (min \"1.5mm + 2.0mm\"))\n"
" (condition \"A.NetClass == 'HV' && B.NetClass == 'HV'\"))\n"
"\n"
"\n"
" (rule HV_unshielded\n"
" (constraint clearance (min 2mm))\n"
" (condition \"A.NetClass == 'HV' && !A.enclosedByArea('Shield*')\"))\n"
"\n"
"\n"
" (rule heavy_thermals\n"
" (constraint thermal_spoke_width (min 0.5mm))\n"
" (condition \"A.NetClass == 'HV'\"))\n"
"<br><br>\n"
"\n"
"### Notes\n"
"\n"
"Version clause must be the first clause. It indicates the syntax version of the file so that \n"
"future rules parsers can perform automatic updates. It should be\n"
"set to \"1\".\n"
"\n"
"Rules should be ordered by specificity. Later rules take\n"
"precedence over earlier rules; once a matching rule is found\n"
"no further rules will be checked.\n"
"\n"
"Use Ctrl+/ to comment or uncomment line(s).\n"
"<br><br><br>\n"
"\n"
"### Expression functions\n"
"\n"
"All function parameters support simple wildcards (`*` and `?`).\n"
"<br><br>\n"
"\n"
" A.intersectsCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's principal courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsFrontCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's front courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsBackCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's back courtyard.\n"
"<br><br>\n"
"\n"
" A.intersectsArea('<zone_name>')\n"
"True if any part of `A` lies within the given zone's outline.\n"
"<br><br>\n"
"\n"
" A.enclosedByArea('<zone_name>')\n"
"True if all of `A` lies within the given zone's outline. \n"
"\n"
"NB: this is potentially a more expensive call than `intersectsArea()`. Use `intersectsArea()` \n"
"where possible.\n"
"<br><br>\n"
"\n"
" A.isPlated()\n"
"True if `A` has a hole which is plated.\n"
"<br><br>\n"
"\n"
" A.inDiffPair('<net_name>')\n"
"True if `A` has a net that is part of the specified differential pair.\n"
"`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n"
"matches items in the `/CLK_P` and `/CLK_N` nets.\n"
"<br><br>\n"
"\n"
" AB.isCoupledDiffPair()\n"
"True if `A` and `B` are members of the same diff pair.\n"
"<br><br>\n"
"\n"
" A.memberOf('<group_name>')\n"
"True if `A` is a member of the given group. Includes nested membership.\n"
"<br><br>\n"
"\n"
" A.existsOnLayer('<layer_name>')\n"
"True if `A` exists on the given layer. The layer name can be\n"
"either the name assigned in Board Setup > Board Editor Layers or\n"
"the canonical name (ie: `F.Cu`).\n"
"\n"
"NB: this returns true if `A` is on the given layer, independently\n"
"of whether or not the rule is being evaluated for that layer.\n"
"For the latter use a `(layer \"layer_name\")` clause in the rule.\n"
"<br><br>\n"
"\n"
" !!! A.insideCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideFrontCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsFrontCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideBackCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsBackCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideArea('<zone_name>') !!!\n"
"Deprecated; use `intersectsArea()` instead.\n"
"<br><br><br>\n"
"\n"
"### More Examples\n"
"\n"
" (rule \"copper keepout\"\n"
" (constraint disallow track via zone)\n"
" (condition \"A.intersectsArea('zone3')\"))\n"
"\n"
"\n"
" (rule \"BGA neckdown\"\n"
" (constraint track_width (min 0.2mm) (opt 0.25mm))\n"
" (constraint clearance (min 0.05mm) (opt 0.08mm))\n"
" (condition \"A.intersectsCourtyard('U3')\"))\n"
"\n"
"\n"
" # prevent silk over tented vias\n"
" (rule silk_over_via\n"
" (constraint silk_clearance (min 0.2mm))\n"
" (condition \"A.Type == '*Text' && B.Type == 'Via'\"))\n"
"\n"
"\n"
" (rule \"Distance between Vias of Different Nets\"\n"
" (constraint hole_to_hole (min 0.254mm))\n"
" (condition \"A.Type == 'Via' && B.Type == 'Via' && A.Net != B.Net\"))\n"
"\n"
" (rule \"Clearance between Pads of Different Nets\"\n"
" (constraint clearance (min 3.0mm))\n"
" (condition \"A.Type == 'Pad' && B.Type == 'Pad' && A.Net != B.Net\"))\n"
"\n"
"\n"
" (rule \"Via Hole to Track Clearance\"\n"
" (constraint hole_clearance (min 0.254mm))\n"
" (condition \"A.Type == 'Via' && B.Type == 'Track'\"))\n"
"\n"
" (rule \"Pad to Track Clearance\"\n"
" (constraint clearance (min 0.2mm))\n"
" (condition \"A.Type == 'Pad' && B.Type == 'Track'\"))\n"
"\n"
"\n"
" (rule \"clearance-to-1mm-cutout\"\n"
" (constraint clearance (min 0.8mm))\n"
" (condition \"A.Layer == 'Edge.Cuts' && A.Thickness == 1.0mm\"))\n"
"\n"
"\n"
" (rule \"Max Drill Hole Size Mechanical\"\n"
" (constraint hole_size (max 6.3mm))\n"
" (condition \"A.Pad_Type == 'NPTH, mechanical'\"))\n"
"\n"
" (rule \"Max Drill Hole Size PTH\"\n"
" (constraint hole_size (max 6.35mm))\n"
" (condition \"A.Pad_Type == 'Through-hole'\"))\n"
"\n"
"\n"
" # Specify an optimal gap for a particular diff-pair\n"
" (rule \"dp clock gap\"\n"
" (constraint diff_pair_gap (opt \"0.8mm\"))\n"
" (condition \"A.inDiffPair('/CLK')\"))\n"
"\n"
" # Specify a larger clearance around any diff-pair\n"
" (rule \"dp clearance\"\n"
" (constraint clearance (min \"1.5mm\"))\n"
" (condition \"A.inDiffPair('*') && !AB.isCoupledDiffPair()\"))\n"
"\n"
"\n"
" # Don't use thermal reliefs on heatsink pads\n"
" (rule heat_sink_pad\n"
" (constraint zone_connection solid)\n"
" (condition \"A.Fabrication_Property == 'Heatsink pad'\"))\n"
"\n"
" # Require all four thermal relief spokes to connect to parent zone\n"
" (rule fully_spoked_pads\n"
" (constraint min_resolved_spokes 4))\n"
"\n"
" # Set thermal relief gap & spoke width for all zones\n"
" (rule defined_relief\n"
" (constraint thermal_relief_gap (min 10mil))\n"
" (constraint thermal_spoke_width (min 12mil)))\n"
"\n"
" # Override thermal relief gap & spoke width for GND and PWR zones\n"
" (rule defined_relief_pwr\n"
" (constraint thermal_relief_gap (min 10mil))\n"
" (constraint thermal_spoke_width (min 12mil))\n"
" (condition \"A.Name == 'zone_GND' || A.Name == 'zone_PWR'\"))\n"
"\n"
"\n"
" # Prevent solder wicking from SMD pads\n"
" (rule holes_in_pads\n"
" (constraint physical_hole_clearance (min 0.2mm))\n"
" (condition \"B.Pad_Type == 'SMD'\"))\n"
"\n"
" # Disallow solder mask margin overrides\n"
" (rule \"disallow solder mask margin overrides\"\n"
" (constraint assertion \"A.Soldermask_Margin_Override == 0mm\")\n"
" (condition \"A.Type == 'Pad'\"))\n"
"\n"
"\n"
" # Enforce a mechanical clearance between components and board edge\n"
" (rule front_mechanical_board_edge_clearance\n"
" (layer \"F.Courtyard\")\n"
" (constraint physical_clearance (min 3mm))\n"
" (condition \"B.Layer == 'Edge.Cuts'\"))\n"
"\n"
"\n"
" # Check current-carrying capacity\n"
" (rule high-current\n"
" (constraint track_width (min 1.0mm))\n"
" (constraint connection_width (min 0.8mm))\n"
" (condition \"A.NetClass == 'Power'\"))" );

View File

@ -897,7 +897,8 @@ class PCB_LAYER_VALUE : public LIBEVAL::VALUE
{
public:
PCB_LAYER_VALUE( PCB_LAYER_ID aLayer ) :
LIBEVAL::VALUE( double( aLayer ) )
LIBEVAL::VALUE( LayerName( aLayer ) ),
m_layer( aLayer )
{};
virtual bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
@ -930,25 +931,25 @@ public:
mask = i->second;
}
PCB_LAYER_ID layerId = ToLAYER_ID( (int) AsDouble() );
return mask.Contains( layerId );
return mask.Contains( m_layer );
}
protected:
PCB_LAYER_ID m_layer;
};
LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
LIBEVAL::VALUE* PCB_EXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
if( m_itemIndex == 2 )
{
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
return PCB_LAYER_VALUE( context->GetLayer() );
}
return new PCB_LAYER_VALUE( context->GetLayer() );
BOARD_ITEM* item = GetObject( aCtx );
if( !item )
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
@ -958,12 +959,12 @@ LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
// simpler "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
// value when the property doesn't appear on a particular object.
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
}
else
{
if( m_type == LIBEVAL::VT_NUMERIC )
return LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
return new LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
else
{
wxString str;
@ -971,7 +972,7 @@ LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
if( !m_isEnum )
{
str = item->Get<wxString>( it->second );
return LIBEVAL::VALUE( str );
return new LIBEVAL::VALUE( str );
}
else
{
@ -979,51 +980,56 @@ LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
bool valid = any.GetAs<wxString>( &str );
if( valid )
return LIBEVAL::VALUE( str );
{
if( it->second->Name() == wxT( "Layer" ) )
return new PCB_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) );
else
return new LIBEVAL::VALUE( str );
}
}
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
}
}
}
LIBEVAL::VALUE PCB_EXPR_NETCLASS_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
LIBEVAL::VALUE* PCB_EXPR_NETCLASS_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
BOARD_ITEM* item = GetObject( aCtx );
if( !item )
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
if( item->IsConnected() )
return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetClassName() );
return new LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetClassName() );
else
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
}
LIBEVAL::VALUE PCB_EXPR_NETNAME_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
LIBEVAL::VALUE* PCB_EXPR_NETNAME_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
BOARD_ITEM* item = GetObject( aCtx );
if( !item )
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
if( item->IsConnected() )
return LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetname() );
return new LIBEVAL::VALUE( static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNetname() );
else
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
}
LIBEVAL::VALUE PCB_EXPR_TYPE_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
LIBEVAL::VALUE* PCB_EXPR_TYPE_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
BOARD_ITEM* item = GetObject( aCtx );
if( !item )
return LIBEVAL::VALUE();
return new LIBEVAL::VALUE();
return LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
return new LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
}

View File

@ -103,7 +103,7 @@ public:
m_matchingTypes[type_hash] = prop;
}
virtual LIBEVAL::VALUE GetValue( LIBEVAL::CONTEXT* aCtx ) override;
LIBEVAL::VALUE* GetValue( LIBEVAL::CONTEXT* aCtx ) override;
BOARD_ITEM* GetObject( const LIBEVAL::CONTEXT* aCtx ) const;
@ -126,7 +126,7 @@ public:
//printf("*** CreateVarRef %p %d\n", this, aItemIndex );
}
LIBEVAL::VALUE GetValue( LIBEVAL::CONTEXT* aCtx ) override;
LIBEVAL::VALUE* GetValue( LIBEVAL::CONTEXT* aCtx ) override;
};
@ -141,7 +141,7 @@ public:
//printf("*** CreateVarRef %p %d\n", this, aItemIndex );
}
LIBEVAL::VALUE GetValue( LIBEVAL::CONTEXT* aCtx ) override;
LIBEVAL::VALUE* GetValue( LIBEVAL::CONTEXT* aCtx ) override;
};
@ -155,7 +155,7 @@ public:
//printf("*** CreateVarRef %p %d\n", this, aItemIndex );
}
LIBEVAL::VALUE GetValue( LIBEVAL::CONTEXT* aCtx ) override;
LIBEVAL::VALUE* GetValue( LIBEVAL::CONTEXT* aCtx ) override;
};